Unescape HTML · Nim 🤝 Fable 5.2 · #1 Fastest

SHA-256 295b8143c22ebdb16dcdfdd8713a3ddcdb5f477be2c506155cae07e4a2087b54

View source (57.1 KiB of Nim)
# unescape_html in Nim with Wasm SIMD: decodes every HTML character reference in UTF-8 text
# the way Python's html.unescape does, and copies all other bytes unchanged.
#
# The design is the one from my C and WebAssembly text entries:
# - 64 zero bytes are written after the input, so no load or loop needs a bounds check.
# - Two stages per 4 KiB window. Stage 1 turns each 64-byte block into a bitmask of its `&`
#   and appends their positions to a list. Stage 2 walks the list: copy the text since the
#   last reference (32 bytes unconditionally, more in a loop), then decode.
# - Inputs without `&` are returned where they are; nothing is copied.
# - A name of up to 7 bytes ending in `;` is looked up without measuring it: the first `;`
#   in the next 8 bytes is found with a SWAR zero-byte test, the bytes up to it are the key,
#   and one multiply indexes an open-addressed table of 16-byte entries (key, value).
# - Longer names go through a second table and one masked 32-byte vector compare.
# - Without `;` only the 106 legacy names match. A filter on the first two bytes gives the
#   longest legacy name that could start there, usually none.
# - Numeric references of up to 7 digits, decimal or hexadecimal, share one branch-free
#   SWAR path.
# - Output and input share one buffer: output from its start, input 256 KiB in. A reference
#   grows by at most 1.2x (`≫⃒` is 5 bytes in, 6 out), so the write position stays at
#   least 51 KiB behind the read position and decoding in place is safe.
# - The entity list is stored packed (5.9 KB for 2,125 names) and unpacked on the first
#   call: see `sym` and `init`. pack_mix.py, at the end of this file, is the matching packer.
#
# Build: see the end of this file.

{.push checks: off, stackTrace: off, lineTrace: off.}

const
  InputCap = 1'u32 shl 20
  # The largest growth is `≫⃒` (5 bytes) becoming 6 bytes.
  OutputCap = InputCap div 5 * 6 + 8
  InOff = 1'u32 shl 18
  Pad = 128
  FastBits = 12
  FastTail = 32
  LegBits = 8
  LegTail = 16
  LongBits = 11
  Empty = not 0'u64
  # Bytes of input per stage-1 window; a multiple of 64.
  Window = 4096'u32
  Ones = 0x0101010101010101'u64
  High = 0x8080808080808080'u64
  K = 1'u32 shl 20

const
  HistSize = 16130
  EntityCount = 2125
  LongCount = 506
  ModelBits = 17
  ModelLimit = 3
  MixShift = 11
  MixW0 = 16384
  FastMul = 0xa59d054b12f5b905'u64
  LegMul = 0xf880d97d1854e551'u64
  LongMul = 0xa0c2995f40498cb3'u64
  # e^(2047 / 256) and e^(-1 / 256) as the bits of a float64.
  SquashE0 = 0x40a732ac33de5528'u64
  SquashStep = 0x3fefe00ffaabffbc'u64
  # Step toward the bit just seen, as a 16-bit fraction: 2 / (2n + 3) after n uses.
  rate: array[4, uint16] = [43690'u16, 26214'u16, 18724'u16, 14563'u16]
  packed: array[5870, uint8] = [
    222'u8, 225, 76, 127, 180, 26, 183, 40, 117, 111, 212, 1, 220, 243, 80, 19, 137, 147, 125, 129, 7, 7, 137, 16,
    130, 158, 138, 125, 5, 234, 196, 106, 89, 69, 197, 243, 136, 86, 218, 152, 109, 108, 69, 33, 55, 71, 159, 88,
    85, 51, 112, 51, 107, 141, 51, 22, 154, 180, 33, 140, 63, 154, 131, 152, 13, 94, 30, 228, 23, 251, 135, 236,
    129, 186, 220, 6, 97, 11, 99, 33, 221, 217, 224, 75, 142, 69, 12, 159, 171, 25, 206, 254, 134, 114, 176, 175,
    132, 228, 146, 96, 98, 30, 154, 222, 150, 71, 243, 66, 148, 174, 133, 30, 142, 131, 177, 159, 133, 130, 160, 123,
    193, 128, 149, 204, 238, 192, 189, 17, 170, 157, 176, 106, 63, 181, 103, 100, 115, 245, 163, 25, 48, 120, 48, 82,
    90, 172, 100, 47, 144, 202, 122, 72, 163, 117, 75, 124, 120, 117, 22, 7, 14, 170, 223, 96, 10, 146, 77, 173,
    18, 191, 89, 4, 244, 115, 7, 79, 210, 9, 33, 56, 59, 215, 241, 153, 59, 36, 189, 73, 82, 224, 139, 90,
    93, 19, 211, 248, 230, 217, 167, 18, 133, 137, 245, 198, 55, 115, 75, 158, 211, 214, 26, 9, 87, 209, 211, 4,
    177, 236, 186, 241, 16, 10, 14, 67, 206, 150, 88, 249, 60, 167, 107, 104, 183, 1, 137, 162, 179, 3, 59, 221,
    152, 48, 54, 68, 233, 158, 100, 183, 147, 198, 110, 208, 213, 22, 150, 10, 176, 18, 116, 190, 192, 78, 15, 48,
    55, 236, 30, 217, 23, 70, 169, 50, 20, 82, 52, 164, 137, 115, 232, 112, 101, 176, 1, 67, 149, 92, 57, 181,
    66, 17, 233, 11, 129, 139, 113, 178, 135, 47, 253, 143, 132, 85, 68, 186, 72, 3, 103, 150, 211, 8, 179, 168,
    70, 203, 65, 169, 107, 64, 171, 34, 180, 33, 131, 16, 18, 136, 202, 27, 222, 53, 101, 51, 101, 248, 245, 158,
    120, 74, 8, 80, 20, 145, 180, 26, 30, 60, 134, 122, 111, 45, 198, 61, 18, 171, 161, 154, 110, 66, 232, 199,
    244, 123, 40, 192, 27, 47, 72, 141, 122, 210, 195, 57, 122, 193, 235, 51, 242, 98, 133, 36, 66, 231, 30, 152,
    141, 128, 122, 49, 254, 46, 254, 29, 158, 140, 157, 104, 190, 7, 159, 239, 249, 30, 135, 181, 86, 112, 132, 137,
    12, 161, 74, 143, 90, 113, 214, 94, 84, 174, 167, 164, 51, 12, 134, 76, 84, 208, 162, 167, 228, 152, 54, 217,
    149, 137, 111, 213, 237, 235, 195, 114, 209, 114, 18, 152, 206, 97, 67, 74, 142, 199, 62, 128, 137, 60, 3, 38,
    70, 140, 231, 253, 149, 133, 239, 165, 223, 61, 165, 184, 244, 24, 157, 223, 170, 85, 3, 191, 64, 75, 86, 23,
    75, 120, 85, 159, 183, 50, 244, 27, 189, 1, 110, 61, 214, 183, 159, 85, 181, 194, 35, 12, 29, 30, 215, 239,
    145, 231, 233, 219, 229, 61, 192, 194, 212, 164, 45, 127, 127, 201, 117, 178, 249, 165, 18, 46, 117, 145, 19, 48,
    247, 250, 227, 61, 25, 22, 103, 32, 68, 206, 47, 236, 169, 202, 175, 228, 179, 213, 254, 111, 10, 62, 177, 47,
    77, 103, 156, 229, 98, 75, 47, 226, 223, 136, 169, 10, 9, 71, 114, 20, 103, 190, 241, 119, 64, 253, 233, 243,
    218, 81, 217, 215, 12, 105, 31, 29, 137, 211, 209, 148, 207, 223, 153, 70, 136, 242, 220, 103, 247, 161, 203, 217,
    212, 126, 218, 98, 232, 107, 247, 116, 230, 194, 84, 137, 6, 65, 201, 239, 2, 42, 134, 114, 1, 141, 237, 208,
    193, 126, 156, 218, 72, 226, 48, 249, 64, 246, 19, 44, 83, 105, 161, 79, 131, 144, 132, 90, 217, 234, 188, 193,
    118, 55, 241, 15, 221, 5, 113, 10, 89, 208, 167, 156, 137, 242, 120, 178, 86, 177, 39, 165, 240, 64, 42, 217,
    43, 5, 179, 28, 220, 244, 197, 135, 196, 131, 109, 185, 88, 158, 47, 125, 233, 98, 86, 13, 110, 112, 47, 47,
    10, 71, 46, 7, 201, 184, 88, 61, 187, 201, 187, 209, 159, 93, 175, 132, 52, 115, 232, 1, 80, 109, 122, 159,
    40, 42, 41, 241, 48, 14, 9, 168, 172, 16, 43, 235, 82, 34, 246, 61, 92, 150, 95, 200, 223, 24, 185, 156,
    187, 170, 139, 27, 248, 230, 66, 157, 150, 44, 248, 160, 131, 147, 137, 156, 107, 181, 47, 13, 27, 69, 148, 78,
    85, 182, 253, 66, 6, 23, 251, 35, 166, 230, 199, 154, 112, 3, 57, 160, 119, 137, 28, 136, 73, 191, 146, 66,
    144, 168, 90, 27, 98, 153, 184, 94, 121, 124, 229, 18, 219, 227, 138, 175, 83, 236, 63, 217, 199, 223, 181, 138,
    223, 213, 207, 98, 28, 31, 239, 237, 100, 75, 50, 131, 123, 122, 107, 70, 127, 125, 120, 119, 69, 176, 143, 200,
    46, 88, 53, 216, 233, 182, 181, 157, 204, 132, 19, 222, 6, 199, 113, 223, 208, 84, 11, 129, 53, 39, 85, 186,
    203, 216, 236, 241, 249, 53, 18, 204, 218, 166, 115, 248, 145, 198, 46, 206, 254, 174, 217, 232, 138, 92, 123, 120,
    71, 48, 58, 72, 157, 6, 170, 203, 94, 173, 53, 195, 84, 15, 225, 240, 64, 220, 236, 204, 105, 54, 97, 109,
    17, 73, 188, 216, 230, 42, 13, 126, 212, 248, 225, 216, 2, 3, 96, 141, 173, 92, 235, 176, 145, 62, 114, 173,
    212, 21, 11, 227, 218, 13, 59, 33, 123, 220, 142, 130, 58, 197, 213, 118, 232, 190, 78, 35, 109, 249, 15, 248,
    16, 236, 150, 185, 238, 155, 31, 222, 70, 254, 82, 244, 88, 149, 57, 16, 73, 8, 204, 72, 57, 236, 170, 54,
    26, 184, 248, 155, 85, 42, 85, 204, 117, 124, 22, 129, 51, 71, 158, 255, 206, 26, 97, 178, 128, 246, 40, 217,
    85, 194, 0, 166, 4, 119, 243, 33, 240, 155, 110, 206, 40, 164, 247, 74, 145, 55, 252, 114, 131, 33, 187, 127,
    204, 42, 245, 20, 11, 24, 107, 200, 9, 21, 168, 165, 226, 210, 67, 186, 156, 112, 90, 140, 10, 228, 176, 168,
    26, 170, 42, 201, 82, 171, 112, 111, 245, 233, 118, 235, 168, 27, 36, 52, 50, 0, 59, 12, 207, 40, 210, 111,
    146, 243, 162, 129, 76, 136, 247, 122, 35, 184, 253, 173, 227, 29, 228, 185, 235, 128, 52, 243, 242, 236, 78, 59,
    239, 195, 116, 252, 229, 116, 151, 85, 2, 1, 23, 247, 108, 6, 198, 139, 164, 24, 110, 0, 63, 51, 238, 37,
    33, 24, 161, 238, 202, 87, 226, 31, 14, 9, 221, 31, 144, 44, 55, 234, 140, 17, 13, 153, 233, 45, 28, 50,
    86, 185, 183, 181, 122, 108, 174, 12, 224, 81, 12, 207, 69, 124, 197, 114, 2, 81, 167, 104, 133, 241, 147, 234,
    129, 130, 169, 161, 254, 89, 28, 0, 214, 48, 34, 97, 181, 246, 67, 135, 250, 120, 9, 43, 218, 52, 126, 45,
    251, 58, 155, 240, 209, 154, 37, 216, 245, 152, 203, 1, 192, 255, 183, 144, 16, 198, 67, 78, 104, 252, 149, 214,
    59, 222, 219, 84, 226, 85, 179, 241, 68, 67, 52, 125, 148, 156, 228, 88, 92, 188, 37, 42, 117, 160, 242, 156,
    234, 153, 217, 242, 120, 244, 47, 115, 114, 150, 90, 177, 199, 30, 178, 56, 225, 251, 184, 103, 116, 115, 122, 163,
    238, 7, 80, 203, 155, 205, 201, 151, 216, 154, 83, 237, 98, 89, 146, 41, 66, 250, 180, 123, 111, 87, 185, 30,
    135, 179, 214, 53, 53, 49, 40, 182, 48, 229, 87, 133, 236, 42, 149, 15, 13, 45, 18, 128, 116, 61, 106, 142,
    217, 86, 245, 243, 251, 219, 224, 75, 102, 16, 46, 80, 27, 157, 55, 27, 204, 224, 172, 4, 164, 194, 147, 218,
    96, 158, 211, 122, 125, 169, 55, 43, 82, 252, 58, 154, 32, 114, 205, 76, 251, 0, 107, 216, 87, 65, 40, 207,
    174, 212, 200, 62, 108, 187, 144, 162, 144, 33, 235, 174, 126, 89, 151, 233, 3, 236, 121, 107, 246, 222, 10, 108,
    83, 250, 144, 37, 98, 3, 49, 236, 83, 101, 71, 120, 49, 206, 186, 242, 126, 199, 94, 28, 50, 151, 146, 231,
    249, 112, 152, 25, 118, 184, 19, 27, 212, 17, 181, 108, 83, 125, 105, 173, 149, 114, 46, 131, 59, 63, 243, 48,
    51, 91, 77, 177, 46, 63, 129, 239, 14, 153, 255, 94, 196, 45, 254, 105, 95, 71, 145, 23, 101, 28, 28, 43,
    253, 71, 28, 203, 37, 96, 103, 233, 85, 127, 239, 37, 120, 244, 55, 121, 29, 192, 92, 51, 161, 203, 113, 148,
    154, 133, 241, 132, 70, 121, 86, 81, 178, 32, 156, 142, 103, 87, 229, 150, 222, 186, 162, 252, 233, 243, 193, 18,
    189, 53, 168, 91, 155, 234, 208, 215, 116, 89, 125, 232, 71, 187, 126, 244, 67, 93, 147, 95, 190, 106, 130, 124,
    16, 65, 75, 43, 219, 206, 86, 36, 228, 214, 91, 4, 238, 223, 120, 80, 125, 100, 33, 92, 155, 217, 150, 31,
    164, 5, 166, 234, 135, 57, 22, 227, 75, 56, 244, 89, 52, 39, 158, 67, 21, 71, 76, 235, 221, 112, 17, 242,
    44, 235, 250, 78, 84, 191, 29, 87, 28, 16, 211, 178, 109, 159, 174, 68, 53, 253, 188, 51, 226, 93, 181, 80,
    151, 239, 11, 63, 96, 243, 228, 67, 10, 92, 162, 52, 120, 159, 20, 105, 194, 155, 69, 68, 71, 119, 128, 152,
    224, 242, 192, 20, 50, 139, 252, 90, 215, 30, 39, 193, 61, 230, 19, 99, 0, 193, 85, 44, 45, 242, 225, 148,
    214, 8, 7, 98, 129, 55, 222, 177, 227, 3, 238, 125, 152, 238, 152, 252, 92, 106, 185, 142, 193, 153, 59, 123,
    106, 117, 189, 78, 210, 53, 53, 89, 226, 255, 132, 242, 74, 60, 146, 223, 49, 237, 59, 243, 52, 164, 225, 146,
    108, 91, 29, 57, 154, 199, 111, 17, 53, 20, 193, 60, 69, 151, 214, 232, 116, 160, 113, 120, 99, 137, 230, 175,
    232, 162, 29, 8, 26, 164, 19, 164, 75, 14, 47, 78, 175, 232, 251, 58, 7, 47, 196, 183, 255, 149, 94, 234,
    12, 177, 64, 200, 210, 61, 73, 17, 147, 211, 3, 49, 98, 252, 44, 17, 207, 23, 10, 133, 220, 164, 111, 204,
    227, 141, 207, 182, 152, 20, 14, 242, 96, 12, 79, 106, 236, 191, 235, 66, 139, 214, 100, 146, 223, 171, 56, 69,
    8, 98, 31, 111, 3, 237, 206, 41, 159, 182, 153, 32, 203, 231, 70, 118, 203, 193, 19, 176, 78, 187, 167, 86,
    248, 118, 7, 221, 59, 106, 33, 78, 150, 5, 153, 20, 82, 165, 77, 114, 122, 66, 105, 147, 206, 176, 9, 243,
    178, 52, 91, 165, 190, 24, 56, 43, 133, 95, 118, 45, 152, 140, 67, 250, 196, 173, 222, 60, 223, 184, 102, 56,
    44, 197, 8, 168, 18, 114, 248, 160, 23, 217, 201, 103, 99, 92, 137, 128, 215, 19, 77, 242, 35, 250, 218, 122,
    41, 124, 20, 5, 108, 127, 47, 240, 93, 255, 239, 121, 103, 144, 59, 114, 183, 173, 215, 163, 210, 112, 207, 235,
    170, 22, 9, 100, 75, 4, 9, 150, 186, 137, 0, 215, 132, 129, 215, 79, 99, 173, 165, 223, 232, 23, 69, 125,
    6, 77, 8, 201, 221, 148, 8, 156, 156, 232, 19, 204, 19, 119, 89, 214, 75, 183, 131, 6, 140, 16, 79, 24,
    92, 175, 71, 162, 141, 143, 205, 182, 49, 136, 242, 241, 3, 205, 204, 69, 186, 181, 53, 126, 248, 239, 31, 97,
    122, 161, 3, 116, 152, 209, 247, 61, 181, 45, 205, 187, 11, 175, 5, 32, 105, 45, 229, 97, 157, 219, 147, 22,
    57, 64, 168, 224, 124, 10, 2, 27, 10, 198, 72, 133, 239, 113, 21, 98, 67, 37, 209, 153, 148, 229, 49, 158,
    143, 48, 56, 231, 181, 119, 145, 224, 78, 1, 233, 90, 81, 85, 243, 8, 150, 158, 67, 169, 120, 90, 171, 104,
    177, 169, 102, 92, 76, 165, 224, 241, 80, 98, 170, 164, 254, 19, 36, 80, 144, 155, 222, 254, 33, 74, 43, 238,
    28, 177, 205, 15, 238, 160, 228, 228, 156, 179, 135, 251, 142, 167, 158, 9, 8, 191, 174, 223, 169, 234, 13, 205,
    107, 133, 250, 78, 178, 66, 32, 1, 146, 7, 35, 102, 60, 31, 36, 93, 218, 194, 3, 3, 54, 3, 35, 143,
    108, 117, 74, 125, 132, 149, 228, 88, 214, 172, 110, 165, 115, 237, 31, 253, 131, 128, 232, 114, 196, 151, 107, 108,
    52, 189, 180, 86, 37, 221, 111, 31, 221, 98, 13, 185, 1, 226, 144, 68, 192, 134, 51, 241, 121, 208, 121, 63,
    196, 139, 186, 98, 234, 110, 27, 47, 248, 58, 184, 99, 185, 22, 167, 174, 170, 107, 234, 59, 76, 3, 110, 205,
    69, 201, 40, 212, 169, 88, 93, 104, 182, 59, 68, 82, 121, 182, 239, 196, 123, 252, 160, 32, 147, 101, 143, 225,
    16, 72, 65, 216, 190, 235, 114, 240, 64, 230, 155, 228, 160, 165, 244, 38, 43, 237, 206, 149, 31, 170, 175, 179,
    44, 162, 154, 201, 224, 250, 87, 173, 20, 108, 66, 10, 127, 154, 81, 189, 202, 126, 0, 61, 232, 189, 52, 105,
    245, 153, 117, 165, 45, 242, 79, 141, 39, 97, 11, 18, 121, 16, 137, 59, 247, 56, 158, 143, 22, 6, 0, 59,
    226, 48, 37, 243, 190, 180, 254, 79, 196, 225, 199, 56, 5, 94, 177, 222, 103, 11, 192, 72, 63, 137, 90, 250,
    232, 179, 75, 47, 221, 108, 137, 197, 239, 31, 127, 154, 142, 239, 208, 249, 240, 51, 38, 246, 66, 213, 108, 60,
    104, 220, 169, 76, 134, 214, 146, 233, 145, 234, 200, 14, 87, 87, 65, 193, 46, 10, 213, 45, 37, 172, 94, 119,
    44, 7, 213, 151, 103, 171, 51, 7, 162, 78, 36, 178, 174, 115, 156, 192, 159, 244, 133, 161, 137, 136, 252, 139,
    61, 159, 175, 67, 68, 43, 50, 62, 161, 32, 28, 74, 242, 139, 85, 33, 194, 1, 173, 45, 220, 67, 34, 176,
    11, 108, 129, 133, 26, 105, 200, 68, 38, 129, 244, 168, 67, 223, 58, 103, 93, 242, 75, 181, 189, 116, 207, 159,
    22, 48, 30, 73, 148, 85, 254, 2, 45, 144, 128, 170, 80, 94, 175, 210, 177, 22, 41, 168, 170, 26, 237, 5,
    0, 148, 252, 132, 79, 204, 230, 179, 31, 223, 232, 37, 185, 23, 247, 140, 85, 202, 183, 92, 227, 81, 91, 107,
    157, 1, 56, 158, 231, 139, 245, 113, 164, 203, 206, 230, 127, 179, 83, 223, 101, 226, 227, 222, 190, 37, 172, 75,
    222, 21, 126, 107, 170, 87, 207, 139, 140, 179, 185, 83, 110, 230, 188, 248, 208, 82, 25, 211, 242, 160, 59, 157,
    226, 0, 0, 18, 214, 140, 110, 191, 14, 121, 79, 94, 10, 196, 253, 10, 18, 232, 193, 176, 239, 183, 101, 172,
    125, 240, 233, 155, 39, 52, 39, 228, 186, 146, 226, 10, 132, 90, 24, 0, 147, 141, 99, 172, 8, 175, 157, 243,
    171, 223, 100, 175, 214, 149, 124, 121, 218, 233, 253, 251, 153, 189, 59, 227, 156, 100, 158, 126, 165, 10, 150, 192,
    27, 153, 176, 152, 41, 32, 229, 92, 218, 171, 107, 38, 58, 193, 132, 77, 69, 147, 153, 105, 41, 123, 226, 87,
    39, 148, 206, 185, 73, 249, 142, 149, 216, 82, 22, 126, 89, 221, 141, 188, 46, 73, 25, 122, 253, 80, 199, 12,
    180, 179, 200, 141, 190, 80, 156, 36, 61, 168, 8, 129, 124, 67, 62, 123, 52, 87, 151, 234, 4, 59, 253, 94,
    144, 62, 81, 42, 48, 214, 181, 15, 239, 161, 92, 209, 212, 162, 235, 43, 48, 231, 53, 253, 217, 110, 97, 188,
    208, 250, 248, 59, 211, 28, 213, 73, 70, 199, 148, 67, 95, 29, 15, 30, 209, 70, 126, 181, 85, 245, 180, 26,
    246, 230, 53, 39, 53, 49, 92, 14, 87, 39, 73, 140, 127, 155, 170, 1, 140, 204, 126, 133, 225, 137, 106, 187,
    245, 70, 162, 52, 250, 88, 207, 14, 239, 83, 7, 66, 134, 229, 176, 139, 221, 78, 49, 30, 209, 120, 127, 67,
    1, 134, 29, 146, 175, 160, 177, 141, 110, 189, 117, 191, 148, 109, 255, 70, 202, 252, 195, 111, 91, 67, 167, 90,
    96, 8, 148, 215, 27, 63, 59, 4, 164, 212, 111, 101, 11, 249, 171, 96, 140, 196, 164, 32, 222, 0, 182, 148,
    233, 195, 237, 38, 188, 107, 19, 151, 229, 108, 238, 144, 194, 119, 7, 221, 213, 36, 23, 107, 157, 177, 13, 242,
    110, 59, 108, 115, 242, 151, 228, 227, 56, 78, 3, 218, 233, 227, 171, 127, 156, 78, 66, 175, 185, 124, 180, 240,
    113, 193, 212, 97, 22, 245, 220, 33, 28, 161, 193, 36, 99, 27, 32, 249, 57, 169, 122, 102, 213, 209, 198, 151,
    207, 171, 129, 38, 35, 105, 79, 230, 246, 166, 167, 5, 2, 139, 197, 11, 151, 121, 228, 104, 244, 247, 113, 57,
    86, 87, 252, 141, 250, 138, 194, 101, 91, 242, 48, 238, 117, 138, 52, 188, 105, 141, 167, 206, 113, 8, 212, 176,
    94, 131, 200, 101, 43, 92, 156, 6, 26, 170, 212, 133, 146, 141, 150, 34, 51, 253, 147, 98, 143, 75, 40, 199,
    210, 34, 99, 155, 224, 90, 71, 129, 70, 211, 196, 21, 2, 66, 74, 179, 240, 11, 60, 178, 158, 197, 26, 6,
    211, 98, 155, 47, 120, 216, 2, 206, 243, 165, 37, 227, 159, 171, 171, 213, 88, 244, 80, 253, 138, 208, 180, 237,
    154, 31, 225, 88, 49, 160, 111, 90, 219, 141, 193, 220, 56, 9, 47, 46, 85, 254, 170, 193, 122, 17, 35, 230,
    224, 193, 219, 60, 120, 204, 80, 200, 206, 205, 167, 45, 80, 222, 111, 95, 243, 23, 65, 194, 54, 41, 129, 25,
    74, 49, 252, 233, 197, 77, 195, 78, 142, 219, 172, 73, 46, 238, 90, 215, 50, 86, 70, 159, 35, 188, 207, 171,
    4, 89, 255, 249, 13, 217, 104, 16, 75, 96, 36, 230, 158, 42, 113, 165, 95, 144, 35, 213, 188, 88, 11, 19,
    41, 99, 151, 62, 57, 118, 103, 130, 46, 176, 210, 145, 141, 142, 110, 174, 113, 159, 17, 162, 28, 48, 23, 250,
    135, 218, 17, 254, 126, 39, 0, 69, 177, 54, 122, 57, 243, 45, 149, 149, 227, 3, 213, 51, 23, 25, 189, 105,
    108, 238, 43, 103, 52, 71, 58, 72, 220, 229, 160, 90, 86, 184, 54, 207, 211, 233, 93, 79, 191, 71, 193, 129,
    251, 138, 103, 144, 172, 6, 190, 118, 231, 129, 213, 103, 187, 224, 205, 97, 101, 19, 164, 66, 128, 53, 31, 188,
    11, 194, 161, 251, 95, 218, 225, 85, 52, 82, 151, 81, 5, 191, 89, 243, 46, 98, 21, 0, 22, 43, 76, 1,
    124, 230, 163, 71, 115, 19, 175, 137, 128, 164, 225, 111, 89, 253, 124, 172, 60, 54, 35, 72, 97, 194, 4, 8,
    97, 84, 137, 141, 221, 140, 91, 191, 49, 27, 44, 66, 254, 11, 122, 161, 16, 134, 174, 200, 131, 95, 36, 146,
    56, 189, 136, 144, 32, 168, 7, 97, 209, 222, 67, 255, 71, 174, 132, 254, 29, 239, 58, 175, 41, 161, 174, 48,
    67, 194, 24, 193, 172, 183, 14, 127, 145, 32, 226, 75, 251, 146, 180, 205, 75, 183, 70, 107, 219, 43, 130, 212,
    101, 238, 255, 140, 14, 102, 46, 183, 129, 225, 227, 156, 56, 7, 92, 67, 13, 4, 22, 103, 41, 28, 69, 170,
    203, 36, 213, 22, 155, 23, 52, 84, 112, 22, 42, 1, 81, 32, 95, 98, 8, 59, 239, 61, 103, 183, 226, 5,
    245, 19, 175, 246, 1, 20, 186, 177, 202, 97, 185, 217, 61, 254, 190, 220, 211, 144, 151, 39, 247, 62, 109, 151,
    102, 40, 142, 99, 120, 233, 62, 244, 210, 221, 63, 174, 22, 34, 105, 72, 32, 103, 226, 194, 80, 31, 190, 201,
    133, 40, 0, 13, 88, 99, 90, 203, 251, 10, 194, 111, 255, 100, 75, 211, 198, 70, 43, 226, 175, 2, 101, 74,
    198, 104, 136, 67, 139, 123, 238, 109, 196, 103, 44, 243, 0, 120, 131, 170, 233, 68, 18, 124, 86, 175, 242, 204,
    89, 139, 75, 146, 26, 167, 125, 176, 222, 36, 123, 81, 188, 96, 168, 151, 55, 231, 103, 108, 88, 231, 50, 5,
    103, 1, 130, 97, 125, 228, 4, 236, 7, 162, 44, 190, 199, 163, 212, 60, 24, 244, 18, 155, 139, 114, 242, 243,
    164, 238, 94, 62, 151, 63, 124, 87, 207, 169, 34, 91, 22, 73, 184, 18, 211, 31, 229, 74, 6, 66, 1, 159,
    128, 31, 114, 248, 157, 0, 86, 5, 255, 80, 132, 76, 146, 115, 198, 127, 50, 154, 83, 139, 199, 130, 87, 153,
    232, 133, 94, 162, 166, 235, 114, 108, 236, 13, 45, 17, 47, 71, 2, 241, 45, 183, 12, 29, 83, 88, 176, 171,
    199, 26, 29, 250, 246, 159, 35, 216, 249, 169, 140, 195, 187, 57, 116, 244, 245, 166, 136, 55, 113, 2, 101, 112,
    33, 187, 141, 200, 207, 116, 143, 71, 9, 78, 124, 103, 150, 236, 251, 119, 18, 247, 10, 58, 246, 244, 71, 151,
    156, 151, 245, 56, 115, 69, 187, 138, 47, 97, 200, 238, 5, 111, 114, 119, 87, 142, 5, 220, 2, 232, 2, 196,
    204, 91, 18, 72, 58, 88, 248, 52, 11, 141, 227, 77, 185, 131, 32, 115, 52, 8, 48, 10, 183, 218, 225, 202,
    193, 202, 122, 152, 250, 16, 166, 12, 192, 41, 113, 3, 198, 44, 89, 247, 107, 86, 193, 162, 9, 152, 12, 197,
    236, 37, 136, 222, 144, 234, 33, 55, 204, 218, 28, 109, 43, 34, 8, 194, 237, 52, 245, 11, 141, 173, 27, 56,
    216, 63, 103, 81, 40, 226, 222, 180, 20, 147, 118, 218, 8, 223, 91, 109, 3, 169, 239, 244, 11, 58, 17, 201,
    192, 56, 86, 84, 31, 90, 29, 200, 153, 240, 8, 65, 238, 14, 52, 246, 254, 85, 167, 168, 176, 206, 44, 153,
    200, 122, 129, 230, 113, 103, 27, 177, 162, 146, 169, 91, 36, 188, 249, 157, 175, 9, 38, 234, 160, 161, 158, 39,
    93, 188, 166, 254, 251, 216, 149, 162, 134, 219, 175, 111, 208, 164, 43, 87, 37, 109, 84, 6, 41, 170, 180, 219,
    211, 64, 13, 119, 45, 232, 212, 45, 116, 114, 158, 210, 248, 108, 13, 243, 232, 106, 250, 34, 209, 169, 187, 25,
    241, 69, 139, 15, 1, 53, 134, 77, 211, 239, 206, 181, 247, 143, 69, 182, 73, 248, 55, 129, 96, 55, 81, 171,
    75, 115, 61, 219, 205, 169, 138, 46, 90, 249, 184, 241, 199, 235, 87, 53, 83, 243, 9, 108, 244, 157, 158, 36,
    244, 67, 244, 89, 59, 169, 184, 209, 37, 184, 190, 141, 125, 4, 74, 9, 142, 206, 181, 252, 155, 43, 254, 15,
    19, 124, 112, 104, 12, 90, 87, 44, 34, 103, 75, 64, 1, 84, 17, 214, 78, 167, 101, 149, 67, 97, 245, 186,
    123, 125, 18, 252, 141, 246, 148, 233, 236, 187, 232, 170, 139, 224, 113, 162, 0, 43, 160, 9, 71, 69, 142, 123,
    40, 250, 25, 187, 28, 29, 165, 241, 48, 42, 209, 185, 97, 35, 171, 45, 124, 96, 109, 251, 20, 181, 211, 176,
    125, 115, 88, 31, 82, 245, 40, 103, 44, 208, 195, 220, 116, 150, 185, 6, 255, 56, 108, 232, 10, 105, 173, 242,
    202, 36, 79, 97, 26, 117, 82, 13, 191, 105, 44, 174, 166, 252, 208, 230, 120, 131, 118, 193, 41, 100, 154, 136,
    201, 96, 227, 7, 130, 203, 174, 160, 29, 220, 233, 148, 43, 230, 6, 86, 88, 85, 121, 71, 105, 49, 175, 139,
    170, 59, 111, 108, 75, 22, 99, 42, 35, 250, 220, 0, 89, 18, 252, 102, 170, 142, 225, 44, 186, 45, 91, 37,
    198, 210, 198, 245, 76, 60, 176, 223, 6, 103, 36, 17, 29, 243, 215, 45, 189, 203, 132, 221, 35, 57, 139, 203,
    53, 223, 194, 35, 210, 173, 141, 27, 87, 115, 167, 236, 217, 93, 190, 99, 216, 149, 229, 220, 46, 114, 96, 78,
    163, 138, 62, 133, 41, 15, 199, 190, 135, 36, 248, 196, 126, 127, 39, 100, 233, 223, 25, 181, 177, 227, 114, 41,
    140, 238, 228, 99, 53, 17, 179, 200, 248, 22, 152, 126, 137, 190, 179, 182, 208, 179, 218, 39, 41, 151, 159, 216,
    82, 159, 129, 31, 166, 17, 120, 200, 37, 191, 47, 244, 94, 132, 243, 1, 8, 197, 59, 80, 68, 170, 4, 167,
    41, 15, 163, 250, 135, 178, 101, 235, 188, 160, 247, 97, 196, 91, 128, 107, 136, 26, 21, 64, 57, 75, 82, 117,
    27, 70, 0, 100, 228, 94, 2, 213, 49, 253, 47, 249, 157, 51, 68, 90, 240, 25, 85, 194, 236, 11, 242, 210,
    1, 208, 228, 99, 130, 210, 151, 198, 210, 36, 89, 196, 138, 147, 17, 180, 88, 97, 214, 142, 169, 11, 144, 214,
    170, 210, 228, 189, 76, 103, 5, 254, 142, 250, 164, 215, 93, 229, 98, 26, 95, 110, 227, 2, 102, 254, 13, 183,
    219, 72, 255, 240, 183, 158, 103, 225, 59, 96, 52, 159, 247, 160, 83, 187, 98, 125, 128, 199, 17, 38, 104, 211,
    79, 37, 151, 96, 21, 254, 96, 19, 79, 254, 220, 213, 200, 86, 241, 120, 8, 149, 145, 248, 87, 132, 133, 86,
    217, 192, 58, 166, 240, 91, 41, 9, 80, 209, 124, 149, 58, 31, 54, 79, 16, 83, 104, 191, 174, 235, 85, 152,
    198, 209, 15, 174, 171, 164, 93, 138, 226, 164, 76, 119, 168, 46, 173, 194, 135, 70, 23, 34, 139, 60, 39, 244,
    195, 103, 208, 233, 216, 250, 173, 141, 254, 129, 210, 125, 216, 97, 171, 112, 70, 127, 154, 15, 65, 74, 157, 136,
    145, 34, 51, 134, 158, 121, 198, 241, 187, 149, 13, 250, 149, 129, 42, 152, 251, 239, 161, 140, 187, 203, 255, 167,
    151, 136, 54, 221, 233, 209, 21, 77, 137, 206, 81, 174, 126, 124, 128, 64, 72, 60, 160, 65, 213, 131, 167, 38,
    35, 19, 168, 22, 71, 188, 71, 75, 241, 195, 129, 10, 33, 238, 47, 153, 0, 167, 116, 125, 69, 129, 134, 23,
    234, 108, 46, 140, 129, 46, 243, 83, 52, 241, 255, 70, 100, 116, 129, 72, 82, 242, 253, 246, 141, 42, 40, 162,
    212, 225, 15, 72, 121, 58, 198, 214, 255, 116, 212, 241, 241, 83, 78, 110, 145, 88, 154, 57, 32, 106, 0, 62,
    176, 169, 65, 168, 251, 244, 153, 80, 75, 238, 75, 203, 228, 152, 23, 21, 1, 172, 45, 64, 34, 218, 5, 163,
    155, 248, 15, 106, 236, 111, 236, 166, 199, 82, 101, 142, 245, 36, 99, 100, 61, 23, 29, 190, 102, 106, 101, 162,
    168, 93, 142, 111, 103, 129, 154, 252, 225, 62, 125, 226, 174, 2, 154, 173, 95, 9, 70, 192, 62, 231, 18, 142,
    240, 153, 233, 128, 48, 168, 152, 33, 3, 225, 60, 7, 118, 217, 14, 17, 169, 65, 228, 97, 31, 97, 40, 149,
    219, 157, 220, 66, 11, 82, 134, 81, 208, 197, 135, 136, 136, 127, 55, 196, 206, 230, 121, 162, 91, 134, 249, 1,
    66, 21, 176, 113, 33, 182, 94, 145, 58, 61, 165, 213, 1, 141, 59, 40, 222, 199, 106, 23, 41, 132, 234, 191,
    208, 38, 220, 125, 129, 155, 65, 170, 196, 204, 26, 115, 70, 45, 126, 97, 33, 193, 64, 30, 17, 208, 205, 49,
    137, 108, 140, 215, 53, 11, 174, 62, 32, 40, 48, 161, 233, 215, 228, 46, 119, 41, 43, 252, 236, 152, 186, 141,
    240, 4, 98, 255, 252, 109, 194, 173, 220, 127, 65, 37, 159, 208, 119, 197, 93, 112, 175, 166, 24, 8, 60, 50,
    138, 75, 65, 28, 75, 214, 45, 173, 107, 8, 116, 54, 249, 141, 122, 136, 245, 206, 169, 59, 231, 215, 70, 115,
    16, 146, 252, 239, 40, 93, 255, 75, 32, 47, 143, 204, 68, 198, 229, 21, 129, 25, 233, 92, 14, 219, 122, 5,
    249, 154, 112, 58, 35, 3, 159, 42, 24, 186, 223, 163, 211, 16, 168, 134, 10, 81, 238, 209, 15, 142, 42, 160,
    187, 74, 12, 251, 43, 80, 121, 94, 90, 155, 237, 189, 21, 55, 95, 170, 6, 39, 210, 249, 46, 37, 222, 231,
    62, 43, 44, 95, 231, 206, 127, 225, 47, 157, 168, 32, 25, 213, 5, 102, 239, 46, 192, 150, 73, 157, 147, 54,
    105, 210, 70, 30, 74, 24, 220, 1, 160, 249, 146, 9, 94, 242, 214, 134, 87, 95, 63, 134, 143, 64, 187, 244,
    1, 211, 138, 93, 203, 16, 159, 143, 64, 53, 226, 14, 162, 195, 94, 41, 240, 252, 183, 58, 41, 206, 109, 82,
    28, 107, 68, 155, 18, 48, 93, 253, 135, 123, 31, 208, 184, 191, 16, 51, 232, 242, 119, 125, 224, 24, 210, 162,
    55, 201, 6, 100, 23, 156, 15, 106, 89, 56, 104, 33, 17, 82, 84, 44, 207, 30, 20, 216, 72, 124, 31, 149,
    206, 76, 35, 205, 31, 144, 42, 25, 190, 96, 213, 143, 79, 255, 2, 46, 218, 54, 117, 26, 192, 204, 148, 110,
    225, 61, 179, 34, 168, 91, 230, 238, 78, 78, 79, 249, 206, 196, 205, 85, 210, 144, 88, 89, 141, 255, 211, 205,
    186, 122, 219, 167, 30, 149, 1, 176, 251, 151, 7, 72, 103, 244, 9, 204, 8, 94, 237, 23, 3, 233, 28, 215,
    143, 83, 173, 196, 138, 235, 195, 191, 0, 19, 94, 195, 248, 223, 190, 115, 184, 79, 13, 1, 161, 172, 219, 37,
    130, 27, 67, 196, 151, 124, 190, 251, 82, 99, 133, 15, 86, 90, 176, 58, 3, 246, 18, 184, 200, 101, 226, 254,
    183, 142, 62, 129, 51, 159, 146, 113, 177, 68, 163, 27, 242, 2, 126, 183, 196, 8, 171, 227, 186, 67, 248, 243,
    137, 60, 153, 168, 249, 60, 127, 106, 190, 231, 189, 222, 59, 35, 58, 92, 142, 177, 234, 28, 87, 143, 48, 172,
    11, 65, 255, 66, 220, 191, 155, 90, 196, 139, 204, 72, 182, 151, 56, 167, 228, 65, 0, 40, 89, 86, 110, 13,
    195, 252, 83, 157, 254, 98, 47, 148, 56, 152, 252, 93, 184, 190, 14, 53, 102, 30, 197, 56, 161, 156, 137, 198,
    115, 146, 87, 147, 215, 216, 68, 103, 153, 183, 147, 123, 19, 185, 46, 233, 195, 80, 242, 79, 127, 102, 12, 238,
    228, 226, 155, 180, 131, 86, 105, 251, 178, 84, 211, 244, 214, 118, 240, 44, 26, 15, 188, 22, 157, 44, 144, 127,
    223, 95, 235, 19, 108, 15, 134, 7, 37, 6, 132, 16, 168, 216, 136, 184, 197, 30, 129, 16, 237, 131, 225, 141,
    180, 121, 108, 211, 15, 54, 74, 73, 113, 185, 165, 243, 213, 148, 22, 2, 82, 39, 84, 39, 69, 0, 225, 169,
    26, 58, 173, 27, 99, 127, 75, 143, 161, 245, 209, 26, 5, 61, 177, 26, 11, 26, 241, 169, 140, 90, 16, 220,
    137, 185, 244, 143, 105, 174, 97, 205, 237, 253, 185, 133, 160, 107, 143, 12, 164, 14, 142, 72, 218, 212, 33, 58,
    210, 43, 244, 235, 132, 114, 127, 83, 177, 12, 132, 171, 45, 230, 14, 118, 119, 201, 251, 227, 230, 91, 144, 220,
    25, 97, 30, 81, 67, 222, 128, 172, 6, 229, 63, 115, 201, 57, 216, 136, 196, 164, 31, 182, 70, 190, 70, 38,
    254, 179, 146, 147, 231, 27, 130, 137, 190, 243, 199, 136, 138, 51, 221, 237, 196, 151, 108, 133, 110, 112, 223, 1,
    7, 222, 213, 135, 12, 157, 158, 140, 235, 115, 221, 197, 65, 35, 143, 246, 48, 98, 6, 100, 123, 183, 121, 170,
    52, 175, 22, 96, 19, 118, 64, 58, 225, 100, 137, 213, 213, 58, 44, 202, 205, 196, 205, 241, 173, 214, 218, 163,
    52, 50, 106, 255, 176, 69, 176, 133, 192, 68, 176, 178, 15, 194, 63, 23, 44, 3, 61, 243, 136, 197, 139, 231,
    219, 220, 32, 48, 184, 175, 26, 1, 150, 125, 149, 255, 249, 40, 40, 118, 135, 169, 41, 108, 222, 4, 14, 198,
    51, 92, 219, 216, 123, 64, 123, 167, 187, 158, 87, 7, 121, 212, 200, 20, 3, 90, 50, 105, 79, 155, 95, 188,
    18, 67, 127, 222, 251, 49, 147, 172, 165, 236, 232, 134, 26, 7, 226, 225, 194, 74, 79, 247, 196, 199, 226, 97,
    80, 244, 117, 228, 158, 53, 10, 13, 19, 72, 200, 65, 254, 48, 95, 179, 115, 70, 122, 0, 22, 113, 140, 196,
    107, 128, 157, 152, 78, 157, 30, 85, 105, 78, 3, 183, 73, 20, 124, 122, 192, 161, 74, 207, 232, 48, 73, 80,
    179, 169, 164, 89, 21, 153, 106, 8, 27, 67, 148, 36, 41, 152, 195, 171, 30, 227, 56, 23, 230, 109, 160, 20,
    226, 195, 254, 226, 196, 103, 52, 167, 200, 90, 214, 139, 97, 106, 161, 218, 173, 3, 86, 61, 123, 222, 0, 51,
    59, 20, 178, 73, 52, 155, 114, 69, 142, 140, 9, 225, 120, 153, 39, 124, 215, 201, 118, 47, 224, 23, 59, 161,
    105, 60, 172, 105, 13, 210, 11, 219, 21, 100, 231, 72, 121, 19, 142, 184, 4, 228, 89, 104, 242, 77, 74, 27,
    215, 211, 29, 237, 216, 55, 25, 60, 63, 174, 128, 137, 0, 40, 130, 183, 189, 250, 90, 173, 149, 162, 136, 25,
    64, 26, 73, 62, 77, 74, 255, 10, 8, 55, 204, 131, 202, 91, 79, 3, 68, 154, 16, 246, 202, 239, 138, 67,
    112, 158, 94, 172, 203, 198, 150, 45, 1, 42, 190, 171, 255, 149, 141, 41, 123, 149, 63, 182, 55, 3, 180, 48,
    209, 57, 115, 31, 188, 173, 190, 193, 34, 211, 63, 2, 79, 175, 248, 14, 39, 218, 104, 117, 238, 4, 110, 90,
    20, 213, 187, 87, 11, 35, 45, 77, 42, 40, 12, 60, 72, 84, 146, 11, 69, 195, 124, 148, 244, 56, 82, 215,
    122, 163, 57, 247, 251, 218, 245, 124, 136, 220, 190, 171, 97, 127, 193, 215, 90, 221, 222, 165, 71, 194, 251, 243,
    224, 75, 66, 101, 39, 122, 220, 160, 243, 11, 172, 163, 166, 148,
  ]

type
  # The value's UTF-8 bytes in the low bytes and its length in the top byte.
  Entry = object
    key, val: uint64
  Long = object
    name: array[32, uint8]
    val: uint64
    len: uint32
    pad: uint32
  V128 {.importc: "v128_t", header: "<wasm_simd128.h>", bycopy.} = object
  Bytes = ptr UncheckedArray[uint8]

proc vload(p: pointer): V128 {.importc: "wasm_v128_load", header: "<wasm_simd128.h>".}
proc vstore(p: pointer, v: V128) {.importc: "wasm_v128_store", header: "<wasm_simd128.h>".}
proc splat(x: int8): V128 {.importc: "wasm_i8x16_splat", header: "<wasm_simd128.h>".}
proc eq8(a, b: V128): V128 {.importc: "wasm_i8x16_eq", header: "<wasm_simd128.h>".}
proc bitmask(a: V128): uint32 {.importc: "wasm_i8x16_bitmask", header: "<wasm_simd128.h>".}
proc vand(a, b: V128): V128 {.importc: "wasm_v128_and", header: "<wasm_simd128.h>".}
proc vor(a, b: V128): V128 {.importc: "wasm_v128_or", header: "<wasm_simd128.h>".}
proc vxor(a, b: V128): V128 {.importc: "wasm_v128_xor", header: "<wasm_simd128.h>".}
proc anyTrue(a: V128): bool {.importc: "wasm_v128_any_true", header: "<wasm_simd128.h>".}
proc ctz64(x: uint64): cint {.importc: "__builtin_ctzll", nodecl.}
proc ctz32(x: uint32): cint {.importc: "__builtin_ctz", nodecl.}
proc popcount64(x: uint64): cint {.importc: "__builtin_popcountll", nodecl.}
proc fill(p: pointer, c: cint, n: csize_t) {.importc: "__builtin_memset", nodecl.}
proc copy(d, s: pointer, n: csize_t) {.importc: "__builtin_memcpy", nodecl.}

var
  # One buffer: output from its start, input InOff bytes in. On the first call its start
  # also holds the unpacker's model, which is why the model has 2^17 16-bit entries.
  buf {.align: 64.}: array[InOff + InputCap + Pad, uint8]
  fast: array[(1 shl FastBits) + FastTail, Entry]
  leg: array[(1 shl LegBits) + LegTail, Entry]
  longs: array[LongCount, Long]
  longSlots: array[1 shl LongBits, uint16]
  # Longest legacy name starting with these two bytes (hashed), or 0.
  legMax: array[4096, uint8]
  pos: array[Window + 16, uint32]
  # Byte j of the 32 loaded at lenMask + 32 - n is 0xFF when j < n.
  lenMask: array[64, uint8]
  ready: bool

const
  # Windows-1252 remapping for references to 0x80-0x9F.
  c1: array[32, uint16] = [
    0x20AC'u16, 0x81, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
    0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x8D, 0x017D, 0x8F,
    0x90, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
    0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x9D, 0x017E, 0x0178]
  utf8Marks: array[5, uint32] = [0'u32, 0, 0x80C0, 0x8080E0, 0x808080F0'u32]

template input: Bytes = cast[Bytes](addr buf[InOff])
template output: Bytes = cast[Bytes](addr buf[0])
template ld64(p: pointer): uint64 = cast[ptr uint64](p)[]
template st64(p: pointer, x: uint64) = cast[ptr uint64](p)[] = x
template st32(p: pointer, x: uint32) = cast[ptr uint32](p)[] = x
template at(p: Bytes, i: uint32): pointer = cast[pointer](addr p[i])
# A constant offset is applied to the pointer, not the index, so that it folds into the
# load or store instruction.
template at(p: Bytes, i: uint32, k: int): pointer = cast[pointer](addr cast[Bytes](addr p[i])[k])
template legSlot(w: uint64): uint32 = ((uint32(w and 0xFFFF)) * 0x9E3779B1'u32) shr 20
template rotl(x: uint64, r: int): uint64 = (x shl r) or (x shr (64 - r))
template longHash(head, tail: uint64, n: uint32): uint32 =
  uint32((((head xor rotl(tail, 29)) + n) * LongMul) shr (64 - LongBits))

# Lay the bits of a code point out as four UTF-8 bytes, then drop the unused leading ones.
template utf8(v: uint32, len: var uint32): uint32 =
  let n = 1'u32 + uint32(v > 0x7F) + uint32(v > 0x7FF) + uint32(v > 0xFFFF)
  let bits = (v shr 18) or ((v shr 12 and 0x3F) shl 8) or ((v shr 6 and 0x3F) shl 16) or ((v and 0x3F) shl 24)
  len = n
  if n == 1: v else: (bits shr (32 - 8 * n)) or utf8Marks[n]

# The entity list is stored packed and read once, on the first call. Entities are ordered
# by code point, so a value is a small step from the one before. A name is coded character
# by character with an adaptive binary range coder (the LZMA one) from six contexts: the
# previous one, two and three characters; a match model that predicts the character that
# followed the last place the previous two were seen; and two that look at the same
# position in the previous entity's name, since neighbours in code point order are often
# named alike (Afr, Bfr; Acy, Bcy; Aacute, Acirc).
#
# Each context has a model entry of 16 bits: a 12-bit probability (stored XOR 0x800, so
# zeroed memory means one half) above a 4-bit count of its uses. The entries' probabilities
# are combined by logistic mixing, as in the PAQ compressors:
# p = squash(sum of weight * stretch(p_i)), stretch(p) = ln(p / (1 - p)), and after the bit
# is known each weight moves by error * stretch(p_i).
var
  squash: array[4095, uint16]
  stretch: array[4096, int16]
  # Five sets of six weights, 16.16 fixed point, stored less MixW0.
  weights: array[5, array[6, int32]]
  ctx: array[6, uint32]
  hist: array[HistSize, uint8]
  last: array[4096, uint16]
  rcIn, rcRange, rcCode: uint32

template model: ptr UncheckedArray[uint16] = cast[ptr UncheckedArray[uint16]](addr buf[0])

proc utf8Once(v: uint32, len: var uint32): uint32 {.noinline.} = utf8(v, len)

proc sym(ws, nctx, nbits: uint32): uint32 {.noinline.} =
  var node = 1'u32
  let top = 1'u32 shl nbits
  let w = addr weights[ws]
  while true:
    var slot {.noinit.}: array[6, uint32]
    var st {.noinit.}: array[6, int32]
    var dot = 0'i32
    for k in 0'u32 ..< nctx:
      let s = ((ctx[k] + node * 0x2545F491'u32) * 0x9E3779B1'u32) shr (32 - ModelBits)
      slot[k] = s
      st[k] = int32(stretch[(model[s] shr 4) xor 0x800])
      dot += st[k] * (w[k] + MixW0)
    dot = ashr(dot, 16) + 2047
    if dot < 0: dot = 0
    if dot > 4094: dot = 4094
    # 12-bit probability that the bit is 1, from 1 to 4095.
    let p = uint32(squash[dot])
    let bound = (rcRange shr 12) * p
    let bit = uint32(rcCode < bound)
    if bit != 0:
      rcRange = bound
    else:
      rcCode -= bound
      rcRange -= bound
    while rcRange < (1'u32 shl 24):
      rcRange = rcRange shl 8
      rcCode = (rcCode shl 8) or packed[rcIn]
      rcIn += 1
    # Move each weight by error * input, and each entry toward the bit just seen by
    # 2 / (2n + 3) after n uses.
    let one = int32(bit shl 12)
    let err = one - int32(p)
    for k in 0'u32 ..< nctx:
      w[k] += ashr(st[k] * err, MixShift)
      let e = uint32(model[slot[k]])
      var q = int32((e shr 4) xor 0x800)
      let n = e and 15
      q += ashr((one - q) * int32(rate[n]), 16)
      model[slot[k]] = uint16(((uint32(q) xor 0x800) shl 4) or (n + uint32(n < ModelLimit)))
    node = node * 2 + bit
    if node >= top: break
  node - top

proc init() {.noinline.} =
  fill(addr fast, 255, csize_t(sizeof(fast)))
  fill(addr leg, 255, csize_t(sizeof(leg)))
  fill(addr lenMask, 255, 32)
  # squash[x + 2047] = 4096 / (1 + e^(-x / 256)) rounded, for x = -2047..2047, and stretch,
  # its inverse, filled in as squash rises. Only IEEE multiply, add and divide are used,
  # so the tables are the same bits the packer had.
  var e = cast[float64](SquashE0)
  var pi = 0'u32
  for x in 0'u32 ..< 4095:
    let v = (uint32(8192.0 / (1.0 + e)) + 1) shr 1
    squash[x] = uint16(v)
    while pi <= v:
      stretch[pi] = int16(int32(x) - 2047)
      pi += 1
    e = e * cast[float64](SquashStep)
  rcIn = 4
  rcRange = not 0'u32
  rcCode = (uint32(packed[0]) shl 24) or (uint32(packed[1]) shl 16) or (uint32(packed[2]) shl 8) or packed[3]
  var cp, nl, start = 0'u32
  var hn = 1'u32
  for r in 0 ..< EntityCount:
    # Step from the previous code point: its bit length, then the bits below the top one.
    ctx[0] = K
    let nb = sym(4, 1, 5)
    var d = nb
    if nb > 1:
      ctx[0] = 2 * K + nb
      d = (1'u32 shl (nb - 1)) + sym(4, 1, nb - 1)
    cp += d
    var vn, vn2 = 0'u32
    var val = uint64(utf8Once(cp, vn))
    # A few values have a second code point.
    ctx[0] = 3 * K + uint32(d == 0)
    if sym(4, 1, 1) != 0:
      ctx[0] = 4 * K
      val = val or (uint64(utf8Once(sym(4, 1, 16), vn2)) shl (8 * vn))
    val = val or (uint64(vn + vn2) shl 56)
    ctx[0] = 5 * K + uint32(cp < 256)
    let legacy = sym(4, 1, 1)
    # Names use a 6-bit alphabet: 0 ends the name, then digits, capitals, small letters.
    var name {.noinit.}: array[40, uint8]
    fill(addr name, 0, 40)
    var n, h, mp, ml = 0'u32
    let prev = start
    start = hn
    while true:
      let m3 = min(ml, 3)
      # The character at this position in the previous entity's name (or, past its end,
      # whatever follows it in hist).
      let pc = uint32(hist[prev + n]) shl 6
      ctx[0] = 8 * K + (h and 63)
      ctx[1] = 9 * K + (h and 4095)
      ctx[2] = 13 * K + (if ml != 0: uint32(hist[mp]) else: 0'u32) * 16 + m3
      ctx[3] = 10 * K + (h and 0x3FFFF)
      ctx[4] = 14 * K + pc + (h and 63)
      ctx[5] = 17 * K + pc + min(n, 3) * 4 + min(d, 3)
      # One set of weights for each state of the match model; set 4 is for the fields above.
      let c = sym(m3, 6, 6)
      if ml != 0 and hist[mp] == c:
        ml += 1
        mp += 1
      else:
        ml = 0
      hist[hn] = uint8(c)
      hn += 1
      if c == 0: break
      name[n] = uint8(c + (if c < 11: 47'u32 elif c < 37: 54'u32 else: 60'u32))
      n += 1
      h = (h shl 6) or c
      if n >= 2:
        let l = addr last[h and 4095]
        if ml == 0 and l[] != 0:
          mp = l[]
          ml = 1
        l[] = uint16(hn)
    if n <= 7:
      # fast: the key is the name and its `;`.
      var key = ld64(addr name) or (59'u64 shl (8 * n))
      var e = uint32((key * FastMul) shr (64 - FastBits))
      while fast[e].key != Empty: e += 1
      fast[e].key = key
      fast[e].val = val
      if legacy != 0:
        # leg: the length is part of the key, so a NUL after a shorter name cannot match.
        key = ld64(addr name) or (uint64(n) shl 56)
        e = uint32((key * LegMul) shr (64 - LegBits))
        while leg[e].key != Empty: e += 1
        leg[e].key = key
        leg[e].val = val
        let s = legSlot(key)  # only the first two bytes matter
        if legMax[s] < n: legMax[s] = uint8(n)
    else:
      copy(addr longs[nl].name, addr name, 32)
      longs[nl].val = val
      longs[nl].len = n
      nl += 1
      var hh = longHash(ld64(addr name), ld64(addr name[n - 8]), n)
      while longSlots[hh] != 0: hh = (hh + 1) and ((1 shl LongBits) - 1)
      longSlots[hh] = uint16(nl)
  ready = true

proc input_ptr(): uint32 {.exportc.} = cast[uint32](addr buf[InOff])
proc input_utf8_cap(): uint32 {.exportc.} = InputCap
proc output_utf8_cap(): uint32 {.exportc.} = OutputCap

proc render(n: uint32): uint64 {.exportc.} =
  if unlikely(not ready): init()
  let size = min(n, InputCap)
  let src = input
  let dst = output
  let zero = splat(0)
  vstore(src.at(size), zero)
  vstore(src.at(size, 16), zero)
  vstore(src.at(size, 32), zero)
  vstore(src.at(size, 48), zero)

  # Stage 1 lists the position of every `&` in a window; stage 2 copies the text between
  # references and decodes each one. Knowing the next position before the current
  # reference is decoded keeps the references independent of each other.
  var e, o, base = 0'u32
  let amp = splat(38)
  let semi = splat(59)
  while base < size:
    let lim = if size - base > Window: base + Window else: size
    var count = 0'u32
    while true:
      var mm = uint64(bitmask(eq8(vload(src.at(base)), amp))) or
        (uint64(bitmask(eq8(vload(src.at(base, 16)), amp))) shl 16) or
        (uint64(bitmask(eq8(vload(src.at(base, 32)), amp))) shl 32) or
        (uint64(bitmask(eq8(vload(src.at(base, 48)), amp))) shl 48)
      let cnt = uint32(popcount64(mm))
      let outp = cast[ptr UncheckedArray[uint32]](addr pos[count])
      # Entries past cnt are garbage and are overwritten or ignored.
      outp[0] = base + uint32(ctz64(mm)); mm = mm and (mm - 1)
      outp[1] = base + uint32(ctz64(mm)); mm = mm and (mm - 1)
      outp[2] = base + uint32(ctz64(mm)); mm = mm and (mm - 1)
      outp[3] = base + uint32(ctz64(mm)); mm = mm and (mm - 1)
      if unlikely(cnt > 4):
        outp[4] = base + uint32(ctz64(mm)); mm = mm and (mm - 1)
        outp[5] = base + uint32(ctz64(mm)); mm = mm and (mm - 1)
        outp[6] = base + uint32(ctz64(mm)); mm = mm and (mm - 1)
        outp[7] = base + uint32(ctz64(mm)); mm = mm and (mm - 1)
        var k = 8'u32
        while unlikely(mm != 0):
          outp[k] = base + uint32(ctz64(mm))
          mm = mm and (mm - 1)
          k += 1
      count += cnt
      base += 64
      if base >= lim: break

    for q in 0'u32 ..< count:
      var i = pos[q]
      # Output and input are one buffer, so the compiler cannot move a load across a store.
      # Everything this reference needs is therefore loaded before the first store.
      let w = ld64(src.at(i, 1))
      # Copy the text since the last reference; src[i] is `&` and i < size.
      block:
        let len = i - e
        let ca = vload(src.at(e))
        let cb = vload(src.at(e, 16))
        vstore(dst.at(o), ca)
        vstore(dst.at(o, 16), cb)
        if len > 32:
          var j = 32'u32
          while true:
            vstore(dst.at(o + j), vload(src.at(e + j)))
            j += 16
            if j >= len: break
        o += len
      block done:
        block bare:
          if uint8(w and 0xFF) == 35:
            # Decimal and hexadecimal share one branch-free path: classify 8 bytes, take
            # the digits before the first other byte, and fold them pairwise.
            let hex = ((uint32(w shr 8) and 0xFF) or 0x20) == 120
            var p = i + 2 + uint32(hex)
            let d = ld64(src.at(p))
            let t = d xor 0x3030303030303030'u64
            let digit = not (((t and not High) + 0x7676767676767676'u64) or t) and High
            let y = d or 0x2020202020202020'u64
            let ty = y and not High
            let letter = (ty + Ones * (0x80 - 97)) and not (ty + Ones * (0x7F - 102)) and not y and High
            let stop = not (digit or (if hex: letter else: 0'u64)) and High
            var v, last: uint32
            if likely(stop != 0):
              let nb = uint32(ctz64(stop)) and 0x38
              if unlikely(nb == 0): break bare
              var x = ((d and 0x0F0F0F0F0F0F0F0F'u64) + 9 * (letter shr 7)) shl (64 - nb)
              x = (x * (if hex: 16'u64 else: 10'u64) + (x shr 8)) and 0x00FF00FF00FF00FF'u64
              x = (x * (if hex: 256'u64 else: 100'u64) + (x shr 16)) and 0x0000FFFF0000FFFF'u64
              v = uint32((x * (if hex: 65536'u64 else: 10000'u64) + (x shr 32)) and 0xFFFFFFFF'u64)
              last = p + (nb shr 3)
            else:
              # Eight or more digits: saturate past U+10FFFF.
              let radix = if hex: 16'u32 else: 10'u32
              v = 0
              while true:
                var c = uint32(src[p]) - 48
                if c > 9:
                  c = ((uint32(src[p]) or 0x20) - 97) + 10
                  if c < 10: c = 16
                if c >= radix: break
                v = v * radix + c
                if v > 0x10FFFF: v = 0x110000
                p += 1
              last = p
            i = last + uint32(src[last] == 59)
            if unlikely(v - 0x80 < 0x20): v = c1[v - 0x80]
            elif unlikely(v == 0 or v - 0xD800 < 0x800 or v > 0x10FFFF): v = 0xFFFD
            var len: uint32
            let word = utf8(v, len)
            st32(dst.at(o), word)
            o += len
            break done
          let t = w xor (Ones * 59)
          let z = (t - Ones) and not t and High
          if likely(z != 0):
            # Bit index of the `;` byte; the key is the name and its `;`.
            let kb = uint32(ctz64(z)) and 0x38
            let key = w and ((2'u64 shl (kb + 7)) - 1)
            # Each probe loop only finds the slot; acting on it afterwards keeps the loop
            # free of exits into the code after the reference.
            var s = uint32((key * FastMul) shr (64 - FastBits))
            var k = fast[s].key
            while unlikely(k != key and k != Empty):
              s += 1
              k = fast[s].key
            if likely(k == key):
              let val = fast[s].val
              st64(dst.at(o), val)
              o += uint32(val shr 56)
              i += 2 + (kb shr 3)
              break done
          else:
            # No `;` in 8 bytes: a name of 8 to 31 bytes needs its `;` in the next 24.
            let s0 = vload(src.at(i, 1))
            let s1 = vload(src.at(i, 17))
            let sm = bitmask(eq8(s0, semi)) or (bitmask(eq8(s1, semi)) shl 16)
            if sm != 0:
              let len = uint32(ctz32(sm))
              var h = longHash(w, ld64(src.at(i + len, -7)), len)
              let m0 = vload(addr lenMask[32 - len])
              let m1 = vload(addr lenMask[48 - len])
              var found = 0'u64
              while true:
                let s = longSlots[h]
                if s == 0: break
                let l = addr longs[s - 1]
                let diff = vor(vxor(vand(s0, m0), vload(addr l.name)), vxor(vand(s1, m1), vload(addr l.name[16])))
                if l.len == len and not anyTrue(diff):
                  found = l.val
                  break
                h = (h + 1) and ((1 shl LongBits) - 1)
              if found != 0:
                st64(dst.at(o), found)
                o += uint32(found shr 56)
                i += 2 + len
                break done
          # Without `;`, the longest legacy name that is a prefix.
          var k = uint32(legMax[legSlot(w)])
          var found = 0'u64
          while k >= 2:
            # The length is part of the key, so a NUL after a shorter name cannot match.
            let key = (w and ((1'u64 shl (8 * k)) - 1)) or (uint64(k) shl 56)
            var s = uint32((key * LegMul) shr (64 - LegBits))
            while leg[s].key != key and leg[s].key != Empty: s += 1
            if leg[s].key == key:
              found = leg[s].val
              break
            k -= 1
          if found != 0:
            st64(dst.at(o), found)
            o += uint32(found shr 56)
            i += 1 + k
            break done
        # A bare `&`.
        dst[o] = 38
        o += 1
        i += 1
      e = i
  # Without a reference the input is the output.
  if e == 0: return (uint64(cast[uint32](addr buf[InOff])) shl 32) or size
  var j = e
  while j < size:
    vstore(dst.at(o + (j - e)), vload(src.at(j)))
    j += 16
  o += size - e
  (uint64(cast[uint32](addr buf[0])) shl 32) or o

{.pop.}

#[ Build (build-nim.sh):

#!/bin/sh
# Generates unescape.nim (the single-file source to submit) from unescape.in.nim and builds
# unescape-nim.wasm: Nim 2.4.0 translates to C, zig cc (clang) compiles that to Wasm.
set -eu
cd "$(dirname "$0")"
python3 gen-small.py unescape.in.nim unescape.nim 17
NIM="mise exec nim@2.4.0 --"
# --os:standalone asks for this file. Checks are off, so neither proc is ever called.
cat > panicoverride.nim <<'PANIC'
proc rawoutput(s: string) = discard
proc panic(s: string) {.noreturn.} =
  while true: discard
PANIC
C="${TMPDIR:-/tmp}/unescape-nimcache"
rm -rf "$C"
$NIM nim c --compileOnly --nimcache:"$C" --cpu:wasm32 --os:standalone --mm:none --threads:off \
  -d:danger -d:noSignalHandler --noMain --app:staticlib unescape.nim
LIB="$(dirname "$($NIM which nim)")/../lib"
zig cc -target wasm32-freestanding -O3 -msimd128 -mbulk-memory -nostdlib -fno-strict-aliasing -I"$LIB" \
  -Wl,--no-entry -Wl,--export-memory -Wl,--export=input_ptr -Wl,--export=input_utf8_cap \
  -Wl,--export=output_utf8_cap -Wl,--export=render \
  -Wl,--initial-memory=1507328 -Wl,--max-memory=1507328 -Wl,-z,stack-size=4096 -Wl,--strip-all \
  -o unescape-nim.wasm "$C"/*.c
ls -l unescape-nim.wasm

pack_mix.py, which produced `packed`:

"""Packs the entity list for unescape.wat: entities ordered by code point, coded with an
adaptive binary range coder. Each bit is predicted from several contexts whose predictions
are combined by a small learned mixer (logistic mixing, as in the PAQ compressors).
$sym and $init in unescape.wat mirror Model, Decoder and Coder exactly.
"""
import html.entities

M32 = 0xFFFFFFFF
K = 1 << 20
HASH_A, HASH_B = 0x2545F491, 0x9E3779B1
# Step toward the bit just seen, as a 16-bit fraction: 2 / (2n + 3) after n uses.
RATE = [43690, 26214, 18724, 14563]
# e^(2047 / 256) and e^(-1 / 256), written out so that every platform uses the same bits.
E0, STEP = float.fromhex('0x1.732ac33de5528p+11'), float.fromhex('0x1.fe00ffaabffbcp-1')

html5 = html.entities.html5
NAMES = {k[:-1]: v for k, v in html5.items() if k.endswith(';')}
LEGACY = {k for k in html5 if not k.endswith(';')}
ALPHA = '\0' + '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
CODE = {c: i for i, c in enumerate(ALPHA)}
ORDER = sorted(NAMES, key=lambda n: ([ord(c) for c in NAMES[n]], n))


def tables():
    """squash[x + 2047] = 4096 / (1 + e^(-x / 256)) rounded, for x = -2047..2047, and
    stretch, its inverse. Only IEEE multiply, add and divide, so every machine agrees."""
    squash, stretch, e, pi = [], [0] * 4096, E0, 0
    for x in range(-2047, 2048):
        v = (int(8192 / (1 + e)) + 1) >> 1
        squash.append(v)
        while pi <= v:
            stretch[pi] = x
            pi += 1
        e *= STEP
    assert pi == 4096 and squash[0] >= 1
    return squash, stretch


class Model:
    """Entries are 16 bits: a 12-bit probability (stored XOR 0x800) and a 4-bit count.
    Weight set 4 is for the code point fields; 0 to 3 are for name characters, chosen
    by how long the match model has been right."""

    def __init__(self, bits=17, limit=3, shift=11, w0=16384, sets=5, nmax=6):
        self.t = [0] * (1 << bits)
        self.bits, self.limit, self.shift, self.w0 = bits, limit, shift, w0
        self.squash, self.stretch = tables()
        self.w = [[0] * nmax for _ in range(sets)]

    def slots(self, ctxs, node):
        return [(((c + node * HASH_A) * HASH_B) & M32) >> (32 - self.bits) for c in ctxs]

    def predict(self, slots, ws):
        """12-bit probability that the bit is 1, and the inputs it was mixed from."""
        st = [self.stretch[(self.t[s] >> 4) ^ 0x800] for s in slots]
        dot = sum((w + self.w0) * x for w, x in zip(self.w[ws], st))
        assert abs(dot) < 1 << 31  # the unpacker adds these up in 32 bits
        dot >>= 16
        return self.squash[min(max(dot, -2047), 2047) + 2047], st

    def update(self, slots, ws, st, p, bit):
        err = (bit << 12) - p
        w = self.w[ws]
        for k, s in enumerate(slots):
            w[k] += (st[k] * err) >> self.shift
            assert abs(w[k]) < 1 << 30
            e = self.t[s]
            q, n = (e >> 4) ^ 0x800, e & 15
            q += (((bit << 12) - q) * RATE[n]) >> 16
            self.t[s] = (q ^ 0x800) << 4 | (n + (n < self.limit))


class Encoder:
    def __init__(self):
        self.low, self.range, self.cache, self.pending, self.out = 0, M32, 0, 1, bytearray()

    def shift_low(self):
        if self.low < 0xFF000000 or self.low > M32:
            carry = self.low >> 32
            byte = self.cache
            while self.pending:
                self.out.append((byte + carry) & 0xFF)
                byte = 0xFF
                self.pending -= 1
            self.cache = self.low >> 24 & 0xFF
        self.pending += 1
        self.low = (self.low << 8) & M32

    def bit(self, p, bit):
        bound = (self.range >> 12) * p
        if bit:
            self.range = bound
        else:
            self.low += bound
            self.range -= bound
        while self.range < 1 << 24:
            self.range = (self.range << 8) & M32
            self.shift_low()

    def finish(self):
        for _ in range(5):
            self.shift_low()
        assert self.out[0] == 0
        return bytes(self.out[1:])


class Decoder:
    def __init__(self, data):
        self.data, self.pos, self.range = data + bytes(8), 4, M32
        self.code = int.from_bytes(data[:4], 'big')

    def bit(self, p):
        bound = (self.range >> 12) * p
        if self.code < bound:
            self.range = bound
            bit = 1
        else:
            self.code -= bound
            self.range -= bound
            bit = 0
        while self.range < 1 << 24:
            self.range = (self.range << 8) & M32
            self.code = (self.code << 8 | self.data[self.pos]) & M32
            self.pos += 1
        return bit


class Coder:
    """Runs the same field sequence for packing (values given) and unpacking (values None)."""

    def __init__(self, data=None, **kw):
        self.m = Model(**kw)
        self.enc = Encoder() if data is None else None
        self.dec = Decoder(data) if data is not None else None
        self.hist, self.last, self.start = [0], [0] * 4096, 0

    def sym(self, ctxs, nbits, ws, value=None):
        node = 1
        for i in range(nbits - 1, -1, -1):
            slots = self.m.slots(ctxs, node)
            p, st = self.m.predict(slots, ws)
            if self.enc:
                b = value >> i & 1
                self.enc.bit(p, b)
            else:
                b = self.dec.bit(p)
            self.m.update(slots, ws, st, p, b)
            node = node * 2 + b
        return node - (1 << nbits)

    def entity(self, cp, rec=None):
        """rec = (name, [code points], legacy) when packing. Returns the same when unpacking."""
        name, cps, legacy = rec if rec else (None, None, None)
        d = cps[0] - cp if rec else None
        nb = self.sym([1 * K], 5, 4, d.bit_length() if rec else None)
        if nb > 1:
            d = (1 << (nb - 1)) + self.sym([2 * K + nb], nb - 1, 4, d - (1 << (nb - 1)) if rec else None)
        else:
            d = nb
        cp += d
        second = 0
        if self.sym([3 * K + (d == 0)], 1, 4, len(cps) - 1 if rec else None):
            second = self.sym([4 * K], 16, 4, cps[1] if rec else None)
        leg = self.sym([5 * K + (cp < 256)], 1, 4, int(legacy) if rec else None)
        # The match model predicts the character that followed the last place the previous
        # two characters were seen, and how many predictions in a row have been right.
        # prev walks the previous entity's name in step with this one.
        h, out, mp, ml, prev = 0, '', 0, 0, self.start
        self.start = len(self.hist)
        for i in range(64):
            pc = self.hist[prev + i]
            ctxs = [8 * K + (h & 63), 9 * K + (h & 4095),
                    13 * K + (self.hist[mp] if ml else 0) * 16 + min(ml, 3), 10 * K + (h & 0x3FFFF),
                    14 * K + pc * 64 + (h & 63), 17 * K + pc * 64 + min(i, 3) * 4 + min(d, 3)]
            c = self.sym(ctxs, 6, min(ml, 3), CODE[(name + '\0')[i]] if rec else None)
            if ml and self.hist[mp] == c:
                ml += 1
                mp += 1
            else:
                ml = 0
            self.hist.append(c)
            if c == 0:
                break
            out += ALPHA[c]
            h = h << 6 | c
            if i >= 1:
                if ml == 0 and self.last[h & 4095]:
                    mp, ml = self.last[h & 4095], 1
                self.last[h & 4095] = len(self.hist)
        return out, [cp, second] if second else [cp], bool(leg)


def records():
    return [(n, [ord(c) for c in NAMES[n]], n in LEGACY) for n in ORDER]


def pack(**kw):
    c = Coder(**kw)
    cp = 0
    for rec in records():
        assert len(rec[1]) <= 2 and rec[1][0] >= cp and (len(rec[1]) == 1 or rec[1][1] < 0x10000)
        c.entity(cp, rec)
        cp = rec[1][0]
    return c.enc.finish()


def unpack(data, count, **kw):
    c = Coder(data, **kw)
    cp, out = 0, []
    for _ in range(count):
        rec = c.entity(cp)
        cp = rec[1][0]
        out.append(rec)
    return out


if __name__ == '__main__':
    blob = pack()
    assert unpack(blob, len(ORDER)) == records()
    print('round trip ok', len(blob))
]#

Run it on the example input with qipdo (Node.js 22 or newer):

printf '%s' 'Fish &amp; chips &mdash; caf&eacute; &lt;3 &#x1F600; &copy 2026 &notit;' | npx qipdo calculated.world components/295b8143c22ebdb16dcdfdd8713a3ddcdb5f477be2c506155cae07e4a2087b54.wasm

Nim with Wasm SIMD through the clang intrinsics, no runtime or allocator. Same two-stage decoder as my C entry, decoding in place in one buffer (23 pages at the full 1 MiB input). The entity list is stored packed in 5.9 KB and unpacked on the first call by a context-mixing range decoder with learned logistic mixing weights; the packer is appended to the source.

Builds on
Decoder design from my C entry 882dd447 and WAT entry 147c6682; the single in-place buffer follows my earlier entry 4a6e243a. Logistic mixing is the technique of the PAQ compressors (Matt Mahoney); the range coder is the LZMA one.

Build details

Compiler, size, memory and checksum
Submitted
11 November 2026 at 07:07 UTC
Language
Nim
Compiler
Nim 2.4.0 (C backend) + zig cc 0.15.2 (clang) nim c --compileOnly --cpu:wasm32 --os:standalone --mm:none -d:danger --noMain; zig cc -target wasm32-freestanding -O3 -msimd128 -mbulk-memory -nostdlib -fno-strict-aliasing (full script at the end of the source)
Size
11.2 KiB, gzip 9.1 KiB
Download
~150 ms on Slow 4G (Lighthouse’s 1.6 Mbps, 150 ms round trips)
Memory
23 pages (1.4 MiB)
SIMD
yes
AI models
claude-fable-5-2
Functions, loops, branches and other static counts
Functions
7
Instructions
2,372
Loops
24
Conditional branches
72
Indirect calls
0
Memory loads
49
Memory stores
58
SIMD instructions
90
Potentially trapping instructions
148

Instruction sites in the binary, not executions, counted by qip.dev’s wasm-counts.