From b3545138272324739a20e683f098580b4556467e Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Fri, 19 Jan 2024 20:39:40 +0900 Subject: [PATCH 01/23] rebar3 new lib --- .gitignore | 19 +++++++++++++++++++ rebar.config | 2 ++ src/sqids.app.src | 14 ++++++++++++++ src/sqids.erl | 3 +++ 4 files changed, 38 insertions(+) create mode 100644 .gitignore create mode 100644 rebar.config create mode 100644 src/sqids.app.src create mode 100644 src/sqids.erl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f1c4554 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +.rebar3 +_* +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +_build +.idea +*.iml +rebar3.crashdump +*~ diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..f618f3e --- /dev/null +++ b/rebar.config @@ -0,0 +1,2 @@ +{erl_opts, [debug_info]}. +{deps, []}. \ No newline at end of file diff --git a/src/sqids.app.src b/src/sqids.app.src new file mode 100644 index 0000000..7b82dc6 --- /dev/null +++ b/src/sqids.app.src @@ -0,0 +1,14 @@ +{application, sqids, + [{description, "An OTP library"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["MIT License"]}, + {links, []} + ]}. diff --git a/src/sqids.erl b/src/sqids.erl new file mode 100644 index 0000000..f9829e3 --- /dev/null +++ b/src/sqids.erl @@ -0,0 +1,3 @@ +-module(sqids). + +-export([]). From d7fb00cf8b5d1e346c2c0d3075e916bf1a0bf796 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Fri, 19 Jan 2024 21:06:54 +0900 Subject: [PATCH 02/23] add blocklist --- src/sqids_blocklist.erl | 575 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 575 insertions(+) create mode 100644 src/sqids_blocklist.erl diff --git a/src/sqids_blocklist.erl b/src/sqids_blocklist.erl new file mode 100644 index 0000000..55c908b --- /dev/null +++ b/src/sqids_blocklist.erl @@ -0,0 +1,575 @@ +-module(sqids_blocklist). + +-export([ + get_list/0 + ]). + +-export_types([ + blocklist/0 + ]). + +-type blocklist() :: unicode:unicode_binary(). + +-spec get_list() -> blocklist(). +get_list() -> + [ <<"0rgasm">> + , <<"1d10t">> + , <<"1d1ot">> + , <<"1di0t">> + , <<"1diot">> + , <<"1eccacu10">> + , <<"1eccacu1o">> + , <<"1eccacul0">> + , <<"1eccaculo">> + , <<"1mbec11e">> + , <<"1mbec1le">> + , <<"1mbeci1e">> + , <<"1mbecile">> + , <<"a11upat0">> + , <<"a11upato">> + , <<"a1lupat0">> + , <<"a1lupato">> + , <<"aand">> + , <<"ah01e">> + , <<"ah0le">> + , <<"aho1e">> + , <<"ahole">> + , <<"al1upat0">> + , <<"al1upato">> + , <<"allupat0">> + , <<"allupato">> + , <<"ana1">> + , <<"ana1e">> + , <<"anal">> + , <<"anale">> + , <<"anus">> + , <<"arrapat0">> + , <<"arrapato">> + , <<"arsch">> + , <<"arse">> + , <<"ass">> + , <<"b00b">> + , <<"b00be">> + , <<"b01ata">> + , <<"b0ceta">> + , <<"b0iata">> + , <<"b0ob">> + , <<"b0obe">> + , <<"b0sta">> + , <<"b1tch">> + , <<"b1te">> + , <<"b1tte">> + , <<"ba1atkar">> + , <<"balatkar">> + , <<"bastard0">> + , <<"bastardo">> + , <<"batt0na">> + , <<"battona">> + , <<"bitch">> + , <<"bite">> + , <<"bitte">> + , <<"bo0b">> + , <<"bo0be">> + , <<"bo1ata">> + , <<"boceta">> + , <<"boiata">> + , <<"boob">> + , <<"boobe">> + , <<"bosta">> + , <<"bran1age">> + , <<"bran1er">> + , <<"bran1ette">> + , <<"bran1eur">> + , <<"bran1euse">> + , <<"branlage">> + , <<"branler">> + , <<"branlette">> + , <<"branleur">> + , <<"branleuse">> + , <<"c0ck">> + , <<"c0g110ne">> + , <<"c0g11one">> + , <<"c0g1i0ne">> + , <<"c0g1ione">> + , <<"c0gl10ne">> + , <<"c0gl1one">> + , <<"c0gli0ne">> + , <<"c0glione">> + , <<"c0na">> + , <<"c0nnard">> + , <<"c0nnasse">> + , <<"c0nne">> + , <<"c0u111es">> + , <<"c0u11les">> + , <<"c0u1l1es">> + , <<"c0u1lles">> + , <<"c0ui11es">> + , <<"c0ui1les">> + , <<"c0uil1es">> + , <<"c0uilles">> + , <<"c11t">> + , <<"c11t0">> + , <<"c11to">> + , <<"c1it">> + , <<"c1it0">> + , <<"c1ito">> + , <<"cabr0n">> + , <<"cabra0">> + , <<"cabrao">> + , <<"cabron">> + , <<"caca">> + , <<"cacca">> + , <<"cacete">> + , <<"cagante">> + , <<"cagar">> + , <<"cagare">> + , <<"cagna">> + , <<"cara1h0">> + , <<"cara1ho">> + , <<"caracu10">> + , <<"caracu1o">> + , <<"caracul0">> + , <<"caraculo">> + , <<"caralh0">> + , <<"caralho">> + , <<"cazz0">> + , <<"cazz1mma">> + , <<"cazzata">> + , <<"cazzimma">> + , <<"cazzo">> + , <<"ch00t1a">> + , <<"ch00t1ya">> + , <<"ch00tia">> + , <<"ch00tiya">> + , <<"ch0d">> + , <<"ch0ot1a">> + , <<"ch0ot1ya">> + , <<"ch0otia">> + , <<"ch0otiya">> + , <<"ch1asse">> + , <<"ch1avata">> + , <<"ch1er">> + , <<"ch1ng0">> + , <<"ch1ngadaz0s">> + , <<"ch1ngadazos">> + , <<"ch1ngader1ta">> + , <<"ch1ngaderita">> + , <<"ch1ngar">> + , <<"ch1ngo">> + , <<"ch1ngues">> + , <<"ch1nk">> + , <<"chatte">> + , <<"chiasse">> + , <<"chiavata">> + , <<"chier">> + , <<"ching0">> + , <<"chingadaz0s">> + , <<"chingadazos">> + , <<"chingader1ta">> + , <<"chingaderita">> + , <<"chingar">> + , <<"chingo">> + , <<"chingues">> + , <<"chink">> + , <<"cho0t1a">> + , <<"cho0t1ya">> + , <<"cho0tia">> + , <<"cho0tiya">> + , <<"chod">> + , <<"choot1a">> + , <<"choot1ya">> + , <<"chootia">> + , <<"chootiya">> + , <<"cl1t">> + , <<"cl1t0">> + , <<"cl1to">> + , <<"clit">> + , <<"clit0">> + , <<"clito">> + , <<"cock">> + , <<"cog110ne">> + , <<"cog11one">> + , <<"cog1i0ne">> + , <<"cog1ione">> + , <<"cogl10ne">> + , <<"cogl1one">> + , <<"cogli0ne">> + , <<"coglione">> + , <<"cona">> + , <<"connard">> + , <<"connasse">> + , <<"conne">> + , <<"cou111es">> + , <<"cou11les">> + , <<"cou1l1es">> + , <<"cou1lles">> + , <<"coui11es">> + , <<"coui1les">> + , <<"couil1es">> + , <<"couilles">> + , <<"cracker">> + , <<"crap">> + , <<"cu10">> + , <<"cu1att0ne">> + , <<"cu1attone">> + , <<"cu1er0">> + , <<"cu1ero">> + , <<"cu1o">> + , <<"cul0">> + , <<"culatt0ne">> + , <<"culattone">> + , <<"culer0">> + , <<"culero">> + , <<"culo">> + , <<"cum">> + , <<"cunt">> + , <<"d11d0">> + , <<"d11do">> + , <<"d1ck">> + , <<"d1ld0">> + , <<"d1ldo">> + , <<"damn">> + , <<"de1ch">> + , <<"deich">> + , <<"depp">> + , <<"di1d0">> + , <<"di1do">> + , <<"dick">> + , <<"dild0">> + , <<"dildo">> + , <<"dyke">> + , <<"encu1e">> + , <<"encule">> + , <<"enema">> + , <<"enf01re">> + , <<"enf0ire">> + , <<"enfo1re">> + , <<"enfoire">> + , <<"estup1d0">> + , <<"estup1do">> + , <<"estupid0">> + , <<"estupido">> + , <<"etr0n">> + , <<"etron">> + , <<"f0da">> + , <<"f0der">> + , <<"f0ttere">> + , <<"f0tters1">> + , <<"f0ttersi">> + , <<"f0tze">> + , <<"f0utre">> + , <<"f1ca">> + , <<"f1cker">> + , <<"f1ga">> + , <<"fag">> + , <<"fica">> + , <<"ficker">> + , <<"figa">> + , <<"foda">> + , <<"foder">> + , <<"fottere">> + , <<"fotters1">> + , <<"fottersi">> + , <<"fotze">> + , <<"foutre">> + , <<"fr0c10">> + , <<"fr0c1o">> + , <<"fr0ci0">> + , <<"fr0cio">> + , <<"fr0sc10">> + , <<"fr0sc1o">> + , <<"fr0sci0">> + , <<"fr0scio">> + , <<"froc10">> + , <<"froc1o">> + , <<"froci0">> + , <<"frocio">> + , <<"frosc10">> + , <<"frosc1o">> + , <<"frosci0">> + , <<"froscio">> + , <<"fuck">> + , <<"g00">> + , <<"g0o">> + , <<"g0u1ne">> + , <<"g0uine">> + , <<"gandu">> + , <<"go0">> + , <<"goo">> + , <<"gou1ne">> + , <<"gouine">> + , <<"gr0gnasse">> + , <<"grognasse">> + , <<"haram1">> + , <<"harami">> + , <<"haramzade">> + , <<"hund1n">> + , <<"hundin">> + , <<"id10t">> + , <<"id1ot">> + , <<"idi0t">> + , <<"idiot">> + , <<"imbec11e">> + , <<"imbec1le">> + , <<"imbeci1e">> + , <<"imbecile">> + , <<"j1zz">> + , <<"jerk">> + , <<"jizz">> + , <<"k1ke">> + , <<"kam1ne">> + , <<"kamine">> + , <<"kike">> + , <<"leccacu10">> + , <<"leccacu1o">> + , <<"leccacul0">> + , <<"leccaculo">> + , <<"m1erda">> + , <<"m1gn0tta">> + , <<"m1gnotta">> + , <<"m1nch1a">> + , <<"m1nchia">> + , <<"m1st">> + , <<"mam0n">> + , <<"mamahuev0">> + , <<"mamahuevo">> + , <<"mamon">> + , <<"masturbat10n">> + , <<"masturbat1on">> + , <<"masturbate">> + , <<"masturbati0n">> + , <<"masturbation">> + , <<"merd0s0">> + , <<"merd0so">> + , <<"merda">> + , <<"merde">> + , <<"merdos0">> + , <<"merdoso">> + , <<"mierda">> + , <<"mign0tta">> + , <<"mignotta">> + , <<"minch1a">> + , <<"minchia">> + , <<"mist">> + , <<"musch1">> + , <<"muschi">> + , <<"n1gger">> + , <<"neger">> + , <<"negr0">> + , <<"negre">> + , <<"negro">> + , <<"nerch1a">> + , <<"nerchia">> + , <<"nigger">> + , <<"orgasm">> + , <<"p00p">> + , <<"p011a">> + , <<"p01la">> + , <<"p0l1a">> + , <<"p0lla">> + , <<"p0mp1n0">> + , <<"p0mp1no">> + , <<"p0mpin0">> + , <<"p0mpino">> + , <<"p0op">> + , <<"p0rca">> + , <<"p0rn">> + , <<"p0rra">> + , <<"p0uff1asse">> + , <<"p0uffiasse">> + , <<"p1p1">> + , <<"p1pi">> + , <<"p1r1a">> + , <<"p1rla">> + , <<"p1sc10">> + , <<"p1sc1o">> + , <<"p1sci0">> + , <<"p1scio">> + , <<"p1sser">> + , <<"pa11e">> + , <<"pa1le">> + , <<"pal1e">> + , <<"palle">> + , <<"pane1e1r0">> + , <<"pane1e1ro">> + , <<"pane1eir0">> + , <<"pane1eiro">> + , <<"panele1r0">> + , <<"panele1ro">> + , <<"paneleir0">> + , <<"paneleiro">> + , <<"patakha">> + , <<"pec0r1na">> + , <<"pec0rina">> + , <<"pecor1na">> + , <<"pecorina">> + , <<"pen1s">> + , <<"pendej0">> + , <<"pendejo">> + , <<"penis">> + , <<"pip1">> + , <<"pipi">> + , <<"pir1a">> + , <<"pirla">> + , <<"pisc10">> + , <<"pisc1o">> + , <<"pisci0">> + , <<"piscio">> + , <<"pisser">> + , <<"po0p">> + , <<"po11a">> + , <<"po1la">> + , <<"pol1a">> + , <<"polla">> + , <<"pomp1n0">> + , <<"pomp1no">> + , <<"pompin0">> + , <<"pompino">> + , <<"poop">> + , <<"porca">> + , <<"porn">> + , <<"porra">> + , <<"pouff1asse">> + , <<"pouffiasse">> + , <<"pr1ck">> + , <<"prick">> + , <<"pussy">> + , <<"put1za">> + , <<"puta">> + , <<"puta1n">> + , <<"putain">> + , <<"pute">> + , <<"putiza">> + , <<"puttana">> + , <<"queca">> + , <<"r0mp1ba11e">> + , <<"r0mp1ba1le">> + , <<"r0mp1bal1e">> + , <<"r0mp1balle">> + , <<"r0mpiba11e">> + , <<"r0mpiba1le">> + , <<"r0mpibal1e">> + , <<"r0mpiballe">> + , <<"rand1">> + , <<"randi">> + , <<"rape">> + , <<"recch10ne">> + , <<"recch1one">> + , <<"recchi0ne">> + , <<"recchione">> + , <<"retard">> + , <<"romp1ba11e">> + , <<"romp1ba1le">> + , <<"romp1bal1e">> + , <<"romp1balle">> + , <<"rompiba11e">> + , <<"rompiba1le">> + , <<"rompibal1e">> + , <<"rompiballe">> + , <<"ruff1an0">> + , <<"ruff1ano">> + , <<"ruffian0">> + , <<"ruffiano">> + , <<"s1ut">> + , <<"sa10pe">> + , <<"sa1aud">> + , <<"sa1ope">> + , <<"sacanagem">> + , <<"sal0pe">> + , <<"salaud">> + , <<"salope">> + , <<"saugnapf">> + , <<"sb0rr0ne">> + , <<"sb0rra">> + , <<"sb0rrone">> + , <<"sbattere">> + , <<"sbatters1">> + , <<"sbattersi">> + , <<"sborr0ne">> + , <<"sborra">> + , <<"sborrone">> + , <<"sc0pare">> + , <<"sc0pata">> + , <<"sch1ampe">> + , <<"sche1se">> + , <<"sche1sse">> + , <<"scheise">> + , <<"scheisse">> + , <<"schlampe">> + , <<"schwachs1nn1g">> + , <<"schwachs1nnig">> + , <<"schwachsinn1g">> + , <<"schwachsinnig">> + , <<"schwanz">> + , <<"scopare">> + , <<"scopata">> + , <<"sexy">> + , <<"sh1t">> + , <<"shit">> + , <<"slut">> + , <<"sp0mp1nare">> + , <<"sp0mpinare">> + , <<"spomp1nare">> + , <<"spompinare">> + , <<"str0nz0">> + , <<"str0nza">> + , <<"str0nzo">> + , <<"stronz0">> + , <<"stronza">> + , <<"stronzo">> + , <<"stup1d">> + , <<"stupid">> + , <<"succh1am1">> + , <<"succh1ami">> + , <<"succhiam1">> + , <<"succhiami">> + , <<"sucker">> + , <<"t0pa">> + , <<"tapette">> + , <<"test1c1e">> + , <<"test1cle">> + , <<"testic1e">> + , <<"testicle">> + , <<"tette">> + , <<"topa">> + , <<"tr01a">> + , <<"tr0ia">> + , <<"tr0mbare">> + , <<"tr1ng1er">> + , <<"tr1ngler">> + , <<"tring1er">> + , <<"tringler">> + , <<"tro1a">> + , <<"troia">> + , <<"trombare">> + , <<"turd">> + , <<"twat">> + , <<"vaffancu10">> + , <<"vaffancu1o">> + , <<"vaffancul0">> + , <<"vaffanculo">> + , <<"vag1na">> + , <<"vagina">> + , <<"verdammt">> + , <<"verga">> + , <<"w1chsen">> + , <<"wank">> + , <<"wichsen">> + , <<"x0ch0ta">> + , <<"x0chota">> + , <<"xana">> + , <<"xoch0ta">> + , <<"xochota">> + , <<"z0cc01a">> + , <<"z0cc0la">> + , <<"z0cco1a">> + , <<"z0ccola">> + , <<"z1z1">> + , <<"z1zi">> + , <<"ziz1">> + , <<"zizi">> + , <<"zocc01a">> + , <<"zocc0la">> + , <<"zocco1a">> + , <<"zoccola">> + ] . From 25f376ae2c5a3845ac33c5b114bf19afe4e8a7d5 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Fri, 19 Jan 2024 21:51:13 +0900 Subject: [PATCH 03/23] store blocklist as sets:set() --- src/sqids_blocklist.erl | 1130 +++++++++++++++++++-------------------- 1 file changed, 565 insertions(+), 565 deletions(-) diff --git a/src/sqids_blocklist.erl b/src/sqids_blocklist.erl index 55c908b..9174ffc 100644 --- a/src/sqids_blocklist.erl +++ b/src/sqids_blocklist.erl @@ -1,575 +1,575 @@ -module(sqids_blocklist). -export([ - get_list/0 + get/0 ]). -export_types([ blocklist/0 ]). --type blocklist() :: unicode:unicode_binary(). +-type blocklist() :: sets:set(unicode:latin1_binary()). --spec get_list() -> blocklist(). -get_list() -> - [ <<"0rgasm">> - , <<"1d10t">> - , <<"1d1ot">> - , <<"1di0t">> - , <<"1diot">> - , <<"1eccacu10">> - , <<"1eccacu1o">> - , <<"1eccacul0">> - , <<"1eccaculo">> - , <<"1mbec11e">> - , <<"1mbec1le">> - , <<"1mbeci1e">> - , <<"1mbecile">> - , <<"a11upat0">> - , <<"a11upato">> - , <<"a1lupat0">> - , <<"a1lupato">> - , <<"aand">> - , <<"ah01e">> - , <<"ah0le">> - , <<"aho1e">> - , <<"ahole">> - , <<"al1upat0">> - , <<"al1upato">> - , <<"allupat0">> - , <<"allupato">> - , <<"ana1">> - , <<"ana1e">> - , <<"anal">> - , <<"anale">> - , <<"anus">> - , <<"arrapat0">> - , <<"arrapato">> - , <<"arsch">> - , <<"arse">> - , <<"ass">> - , <<"b00b">> - , <<"b00be">> - , <<"b01ata">> - , <<"b0ceta">> - , <<"b0iata">> - , <<"b0ob">> - , <<"b0obe">> - , <<"b0sta">> - , <<"b1tch">> - , <<"b1te">> - , <<"b1tte">> - , <<"ba1atkar">> - , <<"balatkar">> - , <<"bastard0">> - , <<"bastardo">> - , <<"batt0na">> - , <<"battona">> - , <<"bitch">> - , <<"bite">> - , <<"bitte">> - , <<"bo0b">> - , <<"bo0be">> - , <<"bo1ata">> - , <<"boceta">> - , <<"boiata">> - , <<"boob">> - , <<"boobe">> - , <<"bosta">> - , <<"bran1age">> - , <<"bran1er">> - , <<"bran1ette">> - , <<"bran1eur">> - , <<"bran1euse">> - , <<"branlage">> - , <<"branler">> - , <<"branlette">> - , <<"branleur">> - , <<"branleuse">> - , <<"c0ck">> - , <<"c0g110ne">> - , <<"c0g11one">> - , <<"c0g1i0ne">> - , <<"c0g1ione">> - , <<"c0gl10ne">> - , <<"c0gl1one">> - , <<"c0gli0ne">> - , <<"c0glione">> - , <<"c0na">> - , <<"c0nnard">> - , <<"c0nnasse">> - , <<"c0nne">> - , <<"c0u111es">> - , <<"c0u11les">> - , <<"c0u1l1es">> - , <<"c0u1lles">> - , <<"c0ui11es">> - , <<"c0ui1les">> - , <<"c0uil1es">> - , <<"c0uilles">> - , <<"c11t">> - , <<"c11t0">> - , <<"c11to">> - , <<"c1it">> - , <<"c1it0">> - , <<"c1ito">> - , <<"cabr0n">> - , <<"cabra0">> - , <<"cabrao">> - , <<"cabron">> - , <<"caca">> - , <<"cacca">> - , <<"cacete">> - , <<"cagante">> - , <<"cagar">> - , <<"cagare">> - , <<"cagna">> - , <<"cara1h0">> - , <<"cara1ho">> - , <<"caracu10">> - , <<"caracu1o">> - , <<"caracul0">> - , <<"caraculo">> - , <<"caralh0">> - , <<"caralho">> - , <<"cazz0">> - , <<"cazz1mma">> - , <<"cazzata">> - , <<"cazzimma">> - , <<"cazzo">> - , <<"ch00t1a">> - , <<"ch00t1ya">> - , <<"ch00tia">> - , <<"ch00tiya">> - , <<"ch0d">> - , <<"ch0ot1a">> - , <<"ch0ot1ya">> - , <<"ch0otia">> - , <<"ch0otiya">> - , <<"ch1asse">> - , <<"ch1avata">> - , <<"ch1er">> - , <<"ch1ng0">> - , <<"ch1ngadaz0s">> - , <<"ch1ngadazos">> - , <<"ch1ngader1ta">> - , <<"ch1ngaderita">> - , <<"ch1ngar">> - , <<"ch1ngo">> - , <<"ch1ngues">> - , <<"ch1nk">> - , <<"chatte">> - , <<"chiasse">> - , <<"chiavata">> - , <<"chier">> - , <<"ching0">> - , <<"chingadaz0s">> - , <<"chingadazos">> - , <<"chingader1ta">> - , <<"chingaderita">> - , <<"chingar">> - , <<"chingo">> - , <<"chingues">> - , <<"chink">> - , <<"cho0t1a">> - , <<"cho0t1ya">> - , <<"cho0tia">> - , <<"cho0tiya">> - , <<"chod">> - , <<"choot1a">> - , <<"choot1ya">> - , <<"chootia">> - , <<"chootiya">> - , <<"cl1t">> - , <<"cl1t0">> - , <<"cl1to">> - , <<"clit">> - , <<"clit0">> - , <<"clito">> - , <<"cock">> - , <<"cog110ne">> - , <<"cog11one">> - , <<"cog1i0ne">> - , <<"cog1ione">> - , <<"cogl10ne">> - , <<"cogl1one">> - , <<"cogli0ne">> - , <<"coglione">> - , <<"cona">> - , <<"connard">> - , <<"connasse">> - , <<"conne">> - , <<"cou111es">> - , <<"cou11les">> - , <<"cou1l1es">> - , <<"cou1lles">> - , <<"coui11es">> - , <<"coui1les">> - , <<"couil1es">> - , <<"couilles">> - , <<"cracker">> - , <<"crap">> - , <<"cu10">> - , <<"cu1att0ne">> - , <<"cu1attone">> - , <<"cu1er0">> - , <<"cu1ero">> - , <<"cu1o">> - , <<"cul0">> - , <<"culatt0ne">> - , <<"culattone">> - , <<"culer0">> - , <<"culero">> - , <<"culo">> - , <<"cum">> - , <<"cunt">> - , <<"d11d0">> - , <<"d11do">> - , <<"d1ck">> - , <<"d1ld0">> - , <<"d1ldo">> - , <<"damn">> - , <<"de1ch">> - , <<"deich">> - , <<"depp">> - , <<"di1d0">> - , <<"di1do">> - , <<"dick">> - , <<"dild0">> - , <<"dildo">> - , <<"dyke">> - , <<"encu1e">> - , <<"encule">> - , <<"enema">> - , <<"enf01re">> - , <<"enf0ire">> - , <<"enfo1re">> - , <<"enfoire">> - , <<"estup1d0">> - , <<"estup1do">> - , <<"estupid0">> - , <<"estupido">> - , <<"etr0n">> - , <<"etron">> - , <<"f0da">> - , <<"f0der">> - , <<"f0ttere">> - , <<"f0tters1">> - , <<"f0ttersi">> - , <<"f0tze">> - , <<"f0utre">> - , <<"f1ca">> - , <<"f1cker">> - , <<"f1ga">> - , <<"fag">> - , <<"fica">> - , <<"ficker">> - , <<"figa">> - , <<"foda">> - , <<"foder">> - , <<"fottere">> - , <<"fotters1">> - , <<"fottersi">> - , <<"fotze">> - , <<"foutre">> - , <<"fr0c10">> - , <<"fr0c1o">> - , <<"fr0ci0">> - , <<"fr0cio">> - , <<"fr0sc10">> - , <<"fr0sc1o">> - , <<"fr0sci0">> - , <<"fr0scio">> - , <<"froc10">> - , <<"froc1o">> - , <<"froci0">> - , <<"frocio">> - , <<"frosc10">> - , <<"frosc1o">> - , <<"frosci0">> - , <<"froscio">> - , <<"fuck">> - , <<"g00">> - , <<"g0o">> - , <<"g0u1ne">> - , <<"g0uine">> - , <<"gandu">> - , <<"go0">> - , <<"goo">> - , <<"gou1ne">> - , <<"gouine">> - , <<"gr0gnasse">> - , <<"grognasse">> - , <<"haram1">> - , <<"harami">> - , <<"haramzade">> - , <<"hund1n">> - , <<"hundin">> - , <<"id10t">> - , <<"id1ot">> - , <<"idi0t">> - , <<"idiot">> - , <<"imbec11e">> - , <<"imbec1le">> - , <<"imbeci1e">> - , <<"imbecile">> - , <<"j1zz">> - , <<"jerk">> - , <<"jizz">> - , <<"k1ke">> - , <<"kam1ne">> - , <<"kamine">> - , <<"kike">> - , <<"leccacu10">> - , <<"leccacu1o">> - , <<"leccacul0">> - , <<"leccaculo">> - , <<"m1erda">> - , <<"m1gn0tta">> - , <<"m1gnotta">> - , <<"m1nch1a">> - , <<"m1nchia">> - , <<"m1st">> - , <<"mam0n">> - , <<"mamahuev0">> - , <<"mamahuevo">> - , <<"mamon">> - , <<"masturbat10n">> - , <<"masturbat1on">> - , <<"masturbate">> - , <<"masturbati0n">> - , <<"masturbation">> - , <<"merd0s0">> - , <<"merd0so">> - , <<"merda">> - , <<"merde">> - , <<"merdos0">> - , <<"merdoso">> - , <<"mierda">> - , <<"mign0tta">> - , <<"mignotta">> - , <<"minch1a">> - , <<"minchia">> - , <<"mist">> - , <<"musch1">> - , <<"muschi">> - , <<"n1gger">> - , <<"neger">> - , <<"negr0">> - , <<"negre">> - , <<"negro">> - , <<"nerch1a">> - , <<"nerchia">> - , <<"nigger">> - , <<"orgasm">> - , <<"p00p">> - , <<"p011a">> - , <<"p01la">> - , <<"p0l1a">> - , <<"p0lla">> - , <<"p0mp1n0">> - , <<"p0mp1no">> - , <<"p0mpin0">> - , <<"p0mpino">> - , <<"p0op">> - , <<"p0rca">> - , <<"p0rn">> - , <<"p0rra">> - , <<"p0uff1asse">> - , <<"p0uffiasse">> - , <<"p1p1">> - , <<"p1pi">> - , <<"p1r1a">> - , <<"p1rla">> - , <<"p1sc10">> - , <<"p1sc1o">> - , <<"p1sci0">> - , <<"p1scio">> - , <<"p1sser">> - , <<"pa11e">> - , <<"pa1le">> - , <<"pal1e">> - , <<"palle">> - , <<"pane1e1r0">> - , <<"pane1e1ro">> - , <<"pane1eir0">> - , <<"pane1eiro">> - , <<"panele1r0">> - , <<"panele1ro">> - , <<"paneleir0">> - , <<"paneleiro">> - , <<"patakha">> - , <<"pec0r1na">> - , <<"pec0rina">> - , <<"pecor1na">> - , <<"pecorina">> - , <<"pen1s">> - , <<"pendej0">> - , <<"pendejo">> - , <<"penis">> - , <<"pip1">> - , <<"pipi">> - , <<"pir1a">> - , <<"pirla">> - , <<"pisc10">> - , <<"pisc1o">> - , <<"pisci0">> - , <<"piscio">> - , <<"pisser">> - , <<"po0p">> - , <<"po11a">> - , <<"po1la">> - , <<"pol1a">> - , <<"polla">> - , <<"pomp1n0">> - , <<"pomp1no">> - , <<"pompin0">> - , <<"pompino">> - , <<"poop">> - , <<"porca">> - , <<"porn">> - , <<"porra">> - , <<"pouff1asse">> - , <<"pouffiasse">> - , <<"pr1ck">> - , <<"prick">> - , <<"pussy">> - , <<"put1za">> - , <<"puta">> - , <<"puta1n">> - , <<"putain">> - , <<"pute">> - , <<"putiza">> - , <<"puttana">> - , <<"queca">> - , <<"r0mp1ba11e">> - , <<"r0mp1ba1le">> - , <<"r0mp1bal1e">> - , <<"r0mp1balle">> - , <<"r0mpiba11e">> - , <<"r0mpiba1le">> - , <<"r0mpibal1e">> - , <<"r0mpiballe">> - , <<"rand1">> - , <<"randi">> - , <<"rape">> - , <<"recch10ne">> - , <<"recch1one">> - , <<"recchi0ne">> - , <<"recchione">> - , <<"retard">> - , <<"romp1ba11e">> - , <<"romp1ba1le">> - , <<"romp1bal1e">> - , <<"romp1balle">> - , <<"rompiba11e">> - , <<"rompiba1le">> - , <<"rompibal1e">> - , <<"rompiballe">> - , <<"ruff1an0">> - , <<"ruff1ano">> - , <<"ruffian0">> - , <<"ruffiano">> - , <<"s1ut">> - , <<"sa10pe">> - , <<"sa1aud">> - , <<"sa1ope">> - , <<"sacanagem">> - , <<"sal0pe">> - , <<"salaud">> - , <<"salope">> - , <<"saugnapf">> - , <<"sb0rr0ne">> - , <<"sb0rra">> - , <<"sb0rrone">> - , <<"sbattere">> - , <<"sbatters1">> - , <<"sbattersi">> - , <<"sborr0ne">> - , <<"sborra">> - , <<"sborrone">> - , <<"sc0pare">> - , <<"sc0pata">> - , <<"sch1ampe">> - , <<"sche1se">> - , <<"sche1sse">> - , <<"scheise">> - , <<"scheisse">> - , <<"schlampe">> - , <<"schwachs1nn1g">> - , <<"schwachs1nnig">> - , <<"schwachsinn1g">> - , <<"schwachsinnig">> - , <<"schwanz">> - , <<"scopare">> - , <<"scopata">> - , <<"sexy">> - , <<"sh1t">> - , <<"shit">> - , <<"slut">> - , <<"sp0mp1nare">> - , <<"sp0mpinare">> - , <<"spomp1nare">> - , <<"spompinare">> - , <<"str0nz0">> - , <<"str0nza">> - , <<"str0nzo">> - , <<"stronz0">> - , <<"stronza">> - , <<"stronzo">> - , <<"stup1d">> - , <<"stupid">> - , <<"succh1am1">> - , <<"succh1ami">> - , <<"succhiam1">> - , <<"succhiami">> - , <<"sucker">> - , <<"t0pa">> - , <<"tapette">> - , <<"test1c1e">> - , <<"test1cle">> - , <<"testic1e">> - , <<"testicle">> - , <<"tette">> - , <<"topa">> - , <<"tr01a">> - , <<"tr0ia">> - , <<"tr0mbare">> - , <<"tr1ng1er">> - , <<"tr1ngler">> - , <<"tring1er">> - , <<"tringler">> - , <<"tro1a">> - , <<"troia">> - , <<"trombare">> - , <<"turd">> - , <<"twat">> - , <<"vaffancu10">> - , <<"vaffancu1o">> - , <<"vaffancul0">> - , <<"vaffanculo">> - , <<"vag1na">> - , <<"vagina">> - , <<"verdammt">> - , <<"verga">> - , <<"w1chsen">> - , <<"wank">> - , <<"wichsen">> - , <<"x0ch0ta">> - , <<"x0chota">> - , <<"xana">> - , <<"xoch0ta">> - , <<"xochota">> - , <<"z0cc01a">> - , <<"z0cc0la">> - , <<"z0cco1a">> - , <<"z0ccola">> - , <<"z1z1">> - , <<"z1zi">> - , <<"ziz1">> - , <<"zizi">> - , <<"zocc01a">> - , <<"zocc0la">> - , <<"zocco1a">> - , <<"zoccola">> - ] . +-spec get() -> blocklist(). +get() -> + #{ <<"0rgasm">> => [] + , <<"1d10t">> => [] + , <<"1d1ot">> => [] + , <<"1di0t">> => [] + , <<"1diot">> => [] + , <<"1eccacu10">> => [] + , <<"1eccacu1o">> => [] + , <<"1eccacul0">> => [] + , <<"1eccaculo">> => [] + , <<"1mbec11e">> => [] + , <<"1mbec1le">> => [] + , <<"1mbeci1e">> => [] + , <<"1mbecile">> => [] + , <<"a11upat0">> => [] + , <<"a11upato">> => [] + , <<"a1lupat0">> => [] + , <<"a1lupato">> => [] + , <<"aand">> => [] + , <<"ah01e">> => [] + , <<"ah0le">> => [] + , <<"aho1e">> => [] + , <<"ahole">> => [] + , <<"al1upat0">> => [] + , <<"al1upato">> => [] + , <<"allupat0">> => [] + , <<"allupato">> => [] + , <<"ana1">> => [] + , <<"ana1e">> => [] + , <<"anal">> => [] + , <<"anale">> => [] + , <<"anus">> => [] + , <<"arrapat0">> => [] + , <<"arrapato">> => [] + , <<"arsch">> => [] + , <<"arse">> => [] + , <<"ass">> => [] + , <<"b00b">> => [] + , <<"b00be">> => [] + , <<"b01ata">> => [] + , <<"b0ceta">> => [] + , <<"b0iata">> => [] + , <<"b0ob">> => [] + , <<"b0obe">> => [] + , <<"b0sta">> => [] + , <<"b1tch">> => [] + , <<"b1te">> => [] + , <<"b1tte">> => [] + , <<"ba1atkar">> => [] + , <<"balatkar">> => [] + , <<"bastard0">> => [] + , <<"bastardo">> => [] + , <<"batt0na">> => [] + , <<"battona">> => [] + , <<"bitch">> => [] + , <<"bite">> => [] + , <<"bitte">> => [] + , <<"bo0b">> => [] + , <<"bo0be">> => [] + , <<"bo1ata">> => [] + , <<"boceta">> => [] + , <<"boiata">> => [] + , <<"boob">> => [] + , <<"boobe">> => [] + , <<"bosta">> => [] + , <<"bran1age">> => [] + , <<"bran1er">> => [] + , <<"bran1ette">> => [] + , <<"bran1eur">> => [] + , <<"bran1euse">> => [] + , <<"branlage">> => [] + , <<"branler">> => [] + , <<"branlette">> => [] + , <<"branleur">> => [] + , <<"branleuse">> => [] + , <<"c0ck">> => [] + , <<"c0g110ne">> => [] + , <<"c0g11one">> => [] + , <<"c0g1i0ne">> => [] + , <<"c0g1ione">> => [] + , <<"c0gl10ne">> => [] + , <<"c0gl1one">> => [] + , <<"c0gli0ne">> => [] + , <<"c0glione">> => [] + , <<"c0na">> => [] + , <<"c0nnard">> => [] + , <<"c0nnasse">> => [] + , <<"c0nne">> => [] + , <<"c0u111es">> => [] + , <<"c0u11les">> => [] + , <<"c0u1l1es">> => [] + , <<"c0u1lles">> => [] + , <<"c0ui11es">> => [] + , <<"c0ui1les">> => [] + , <<"c0uil1es">> => [] + , <<"c0uilles">> => [] + , <<"c11t">> => [] + , <<"c11t0">> => [] + , <<"c11to">> => [] + , <<"c1it">> => [] + , <<"c1it0">> => [] + , <<"c1ito">> => [] + , <<"cabr0n">> => [] + , <<"cabra0">> => [] + , <<"cabrao">> => [] + , <<"cabron">> => [] + , <<"caca">> => [] + , <<"cacca">> => [] + , <<"cacete">> => [] + , <<"cagante">> => [] + , <<"cagar">> => [] + , <<"cagare">> => [] + , <<"cagna">> => [] + , <<"cara1h0">> => [] + , <<"cara1ho">> => [] + , <<"caracu10">> => [] + , <<"caracu1o">> => [] + , <<"caracul0">> => [] + , <<"caraculo">> => [] + , <<"caralh0">> => [] + , <<"caralho">> => [] + , <<"cazz0">> => [] + , <<"cazz1mma">> => [] + , <<"cazzata">> => [] + , <<"cazzimma">> => [] + , <<"cazzo">> => [] + , <<"ch00t1a">> => [] + , <<"ch00t1ya">> => [] + , <<"ch00tia">> => [] + , <<"ch00tiya">> => [] + , <<"ch0d">> => [] + , <<"ch0ot1a">> => [] + , <<"ch0ot1ya">> => [] + , <<"ch0otia">> => [] + , <<"ch0otiya">> => [] + , <<"ch1asse">> => [] + , <<"ch1avata">> => [] + , <<"ch1er">> => [] + , <<"ch1ng0">> => [] + , <<"ch1ngadaz0s">> => [] + , <<"ch1ngadazos">> => [] + , <<"ch1ngader1ta">> => [] + , <<"ch1ngaderita">> => [] + , <<"ch1ngar">> => [] + , <<"ch1ngo">> => [] + , <<"ch1ngues">> => [] + , <<"ch1nk">> => [] + , <<"chatte">> => [] + , <<"chiasse">> => [] + , <<"chiavata">> => [] + , <<"chier">> => [] + , <<"ching0">> => [] + , <<"chingadaz0s">> => [] + , <<"chingadazos">> => [] + , <<"chingader1ta">> => [] + , <<"chingaderita">> => [] + , <<"chingar">> => [] + , <<"chingo">> => [] + , <<"chingues">> => [] + , <<"chink">> => [] + , <<"cho0t1a">> => [] + , <<"cho0t1ya">> => [] + , <<"cho0tia">> => [] + , <<"cho0tiya">> => [] + , <<"chod">> => [] + , <<"choot1a">> => [] + , <<"choot1ya">> => [] + , <<"chootia">> => [] + , <<"chootiya">> => [] + , <<"cl1t">> => [] + , <<"cl1t0">> => [] + , <<"cl1to">> => [] + , <<"clit">> => [] + , <<"clit0">> => [] + , <<"clito">> => [] + , <<"cock">> => [] + , <<"cog110ne">> => [] + , <<"cog11one">> => [] + , <<"cog1i0ne">> => [] + , <<"cog1ione">> => [] + , <<"cogl10ne">> => [] + , <<"cogl1one">> => [] + , <<"cogli0ne">> => [] + , <<"coglione">> => [] + , <<"cona">> => [] + , <<"connard">> => [] + , <<"connasse">> => [] + , <<"conne">> => [] + , <<"cou111es">> => [] + , <<"cou11les">> => [] + , <<"cou1l1es">> => [] + , <<"cou1lles">> => [] + , <<"coui11es">> => [] + , <<"coui1les">> => [] + , <<"couil1es">> => [] + , <<"couilles">> => [] + , <<"cracker">> => [] + , <<"crap">> => [] + , <<"cu10">> => [] + , <<"cu1att0ne">> => [] + , <<"cu1attone">> => [] + , <<"cu1er0">> => [] + , <<"cu1ero">> => [] + , <<"cu1o">> => [] + , <<"cul0">> => [] + , <<"culatt0ne">> => [] + , <<"culattone">> => [] + , <<"culer0">> => [] + , <<"culero">> => [] + , <<"culo">> => [] + , <<"cum">> => [] + , <<"cunt">> => [] + , <<"d11d0">> => [] + , <<"d11do">> => [] + , <<"d1ck">> => [] + , <<"d1ld0">> => [] + , <<"d1ldo">> => [] + , <<"damn">> => [] + , <<"de1ch">> => [] + , <<"deich">> => [] + , <<"depp">> => [] + , <<"di1d0">> => [] + , <<"di1do">> => [] + , <<"dick">> => [] + , <<"dild0">> => [] + , <<"dildo">> => [] + , <<"dyke">> => [] + , <<"encu1e">> => [] + , <<"encule">> => [] + , <<"enema">> => [] + , <<"enf01re">> => [] + , <<"enf0ire">> => [] + , <<"enfo1re">> => [] + , <<"enfoire">> => [] + , <<"estup1d0">> => [] + , <<"estup1do">> => [] + , <<"estupid0">> => [] + , <<"estupido">> => [] + , <<"etr0n">> => [] + , <<"etron">> => [] + , <<"f0da">> => [] + , <<"f0der">> => [] + , <<"f0ttere">> => [] + , <<"f0tters1">> => [] + , <<"f0ttersi">> => [] + , <<"f0tze">> => [] + , <<"f0utre">> => [] + , <<"f1ca">> => [] + , <<"f1cker">> => [] + , <<"f1ga">> => [] + , <<"fag">> => [] + , <<"fica">> => [] + , <<"ficker">> => [] + , <<"figa">> => [] + , <<"foda">> => [] + , <<"foder">> => [] + , <<"fottere">> => [] + , <<"fotters1">> => [] + , <<"fottersi">> => [] + , <<"fotze">> => [] + , <<"foutre">> => [] + , <<"fr0c10">> => [] + , <<"fr0c1o">> => [] + , <<"fr0ci0">> => [] + , <<"fr0cio">> => [] + , <<"fr0sc10">> => [] + , <<"fr0sc1o">> => [] + , <<"fr0sci0">> => [] + , <<"fr0scio">> => [] + , <<"froc10">> => [] + , <<"froc1o">> => [] + , <<"froci0">> => [] + , <<"frocio">> => [] + , <<"frosc10">> => [] + , <<"frosc1o">> => [] + , <<"frosci0">> => [] + , <<"froscio">> => [] + , <<"fuck">> => [] + , <<"g00">> => [] + , <<"g0o">> => [] + , <<"g0u1ne">> => [] + , <<"g0uine">> => [] + , <<"gandu">> => [] + , <<"go0">> => [] + , <<"goo">> => [] + , <<"gou1ne">> => [] + , <<"gouine">> => [] + , <<"gr0gnasse">> => [] + , <<"grognasse">> => [] + , <<"haram1">> => [] + , <<"harami">> => [] + , <<"haramzade">> => [] + , <<"hund1n">> => [] + , <<"hundin">> => [] + , <<"id10t">> => [] + , <<"id1ot">> => [] + , <<"idi0t">> => [] + , <<"idiot">> => [] + , <<"imbec11e">> => [] + , <<"imbec1le">> => [] + , <<"imbeci1e">> => [] + , <<"imbecile">> => [] + , <<"j1zz">> => [] + , <<"jerk">> => [] + , <<"jizz">> => [] + , <<"k1ke">> => [] + , <<"kam1ne">> => [] + , <<"kamine">> => [] + , <<"kike">> => [] + , <<"leccacu10">> => [] + , <<"leccacu1o">> => [] + , <<"leccacul0">> => [] + , <<"leccaculo">> => [] + , <<"m1erda">> => [] + , <<"m1gn0tta">> => [] + , <<"m1gnotta">> => [] + , <<"m1nch1a">> => [] + , <<"m1nchia">> => [] + , <<"m1st">> => [] + , <<"mam0n">> => [] + , <<"mamahuev0">> => [] + , <<"mamahuevo">> => [] + , <<"mamon">> => [] + , <<"masturbat10n">> => [] + , <<"masturbat1on">> => [] + , <<"masturbate">> => [] + , <<"masturbati0n">> => [] + , <<"masturbation">> => [] + , <<"merd0s0">> => [] + , <<"merd0so">> => [] + , <<"merda">> => [] + , <<"merde">> => [] + , <<"merdos0">> => [] + , <<"merdoso">> => [] + , <<"mierda">> => [] + , <<"mign0tta">> => [] + , <<"mignotta">> => [] + , <<"minch1a">> => [] + , <<"minchia">> => [] + , <<"mist">> => [] + , <<"musch1">> => [] + , <<"muschi">> => [] + , <<"n1gger">> => [] + , <<"neger">> => [] + , <<"negr0">> => [] + , <<"negre">> => [] + , <<"negro">> => [] + , <<"nerch1a">> => [] + , <<"nerchia">> => [] + , <<"nigger">> => [] + , <<"orgasm">> => [] + , <<"p00p">> => [] + , <<"p011a">> => [] + , <<"p01la">> => [] + , <<"p0l1a">> => [] + , <<"p0lla">> => [] + , <<"p0mp1n0">> => [] + , <<"p0mp1no">> => [] + , <<"p0mpin0">> => [] + , <<"p0mpino">> => [] + , <<"p0op">> => [] + , <<"p0rca">> => [] + , <<"p0rn">> => [] + , <<"p0rra">> => [] + , <<"p0uff1asse">> => [] + , <<"p0uffiasse">> => [] + , <<"p1p1">> => [] + , <<"p1pi">> => [] + , <<"p1r1a">> => [] + , <<"p1rla">> => [] + , <<"p1sc10">> => [] + , <<"p1sc1o">> => [] + , <<"p1sci0">> => [] + , <<"p1scio">> => [] + , <<"p1sser">> => [] + , <<"pa11e">> => [] + , <<"pa1le">> => [] + , <<"pal1e">> => [] + , <<"palle">> => [] + , <<"pane1e1r0">> => [] + , <<"pane1e1ro">> => [] + , <<"pane1eir0">> => [] + , <<"pane1eiro">> => [] + , <<"panele1r0">> => [] + , <<"panele1ro">> => [] + , <<"paneleir0">> => [] + , <<"paneleiro">> => [] + , <<"patakha">> => [] + , <<"pec0r1na">> => [] + , <<"pec0rina">> => [] + , <<"pecor1na">> => [] + , <<"pecorina">> => [] + , <<"pen1s">> => [] + , <<"pendej0">> => [] + , <<"pendejo">> => [] + , <<"penis">> => [] + , <<"pip1">> => [] + , <<"pipi">> => [] + , <<"pir1a">> => [] + , <<"pirla">> => [] + , <<"pisc10">> => [] + , <<"pisc1o">> => [] + , <<"pisci0">> => [] + , <<"piscio">> => [] + , <<"pisser">> => [] + , <<"po0p">> => [] + , <<"po11a">> => [] + , <<"po1la">> => [] + , <<"pol1a">> => [] + , <<"polla">> => [] + , <<"pomp1n0">> => [] + , <<"pomp1no">> => [] + , <<"pompin0">> => [] + , <<"pompino">> => [] + , <<"poop">> => [] + , <<"porca">> => [] + , <<"porn">> => [] + , <<"porra">> => [] + , <<"pouff1asse">> => [] + , <<"pouffiasse">> => [] + , <<"pr1ck">> => [] + , <<"prick">> => [] + , <<"pussy">> => [] + , <<"put1za">> => [] + , <<"puta">> => [] + , <<"puta1n">> => [] + , <<"putain">> => [] + , <<"pute">> => [] + , <<"putiza">> => [] + , <<"puttana">> => [] + , <<"queca">> => [] + , <<"r0mp1ba11e">> => [] + , <<"r0mp1ba1le">> => [] + , <<"r0mp1bal1e">> => [] + , <<"r0mp1balle">> => [] + , <<"r0mpiba11e">> => [] + , <<"r0mpiba1le">> => [] + , <<"r0mpibal1e">> => [] + , <<"r0mpiballe">> => [] + , <<"rand1">> => [] + , <<"randi">> => [] + , <<"rape">> => [] + , <<"recch10ne">> => [] + , <<"recch1one">> => [] + , <<"recchi0ne">> => [] + , <<"recchione">> => [] + , <<"retard">> => [] + , <<"romp1ba11e">> => [] + , <<"romp1ba1le">> => [] + , <<"romp1bal1e">> => [] + , <<"romp1balle">> => [] + , <<"rompiba11e">> => [] + , <<"rompiba1le">> => [] + , <<"rompibal1e">> => [] + , <<"rompiballe">> => [] + , <<"ruff1an0">> => [] + , <<"ruff1ano">> => [] + , <<"ruffian0">> => [] + , <<"ruffiano">> => [] + , <<"s1ut">> => [] + , <<"sa10pe">> => [] + , <<"sa1aud">> => [] + , <<"sa1ope">> => [] + , <<"sacanagem">> => [] + , <<"sal0pe">> => [] + , <<"salaud">> => [] + , <<"salope">> => [] + , <<"saugnapf">> => [] + , <<"sb0rr0ne">> => [] + , <<"sb0rra">> => [] + , <<"sb0rrone">> => [] + , <<"sbattere">> => [] + , <<"sbatters1">> => [] + , <<"sbattersi">> => [] + , <<"sborr0ne">> => [] + , <<"sborra">> => [] + , <<"sborrone">> => [] + , <<"sc0pare">> => [] + , <<"sc0pata">> => [] + , <<"sch1ampe">> => [] + , <<"sche1se">> => [] + , <<"sche1sse">> => [] + , <<"scheise">> => [] + , <<"scheisse">> => [] + , <<"schlampe">> => [] + , <<"schwachs1nn1g">> => [] + , <<"schwachs1nnig">> => [] + , <<"schwachsinn1g">> => [] + , <<"schwachsinnig">> => [] + , <<"schwanz">> => [] + , <<"scopare">> => [] + , <<"scopata">> => [] + , <<"sexy">> => [] + , <<"sh1t">> => [] + , <<"shit">> => [] + , <<"slut">> => [] + , <<"sp0mp1nare">> => [] + , <<"sp0mpinare">> => [] + , <<"spomp1nare">> => [] + , <<"spompinare">> => [] + , <<"str0nz0">> => [] + , <<"str0nza">> => [] + , <<"str0nzo">> => [] + , <<"stronz0">> => [] + , <<"stronza">> => [] + , <<"stronzo">> => [] + , <<"stup1d">> => [] + , <<"stupid">> => [] + , <<"succh1am1">> => [] + , <<"succh1ami">> => [] + , <<"succhiam1">> => [] + , <<"succhiami">> => [] + , <<"sucker">> => [] + , <<"t0pa">> => [] + , <<"tapette">> => [] + , <<"test1c1e">> => [] + , <<"test1cle">> => [] + , <<"testic1e">> => [] + , <<"testicle">> => [] + , <<"tette">> => [] + , <<"topa">> => [] + , <<"tr01a">> => [] + , <<"tr0ia">> => [] + , <<"tr0mbare">> => [] + , <<"tr1ng1er">> => [] + , <<"tr1ngler">> => [] + , <<"tring1er">> => [] + , <<"tringler">> => [] + , <<"tro1a">> => [] + , <<"troia">> => [] + , <<"trombare">> => [] + , <<"turd">> => [] + , <<"twat">> => [] + , <<"vaffancu10">> => [] + , <<"vaffancu1o">> => [] + , <<"vaffancul0">> => [] + , <<"vaffanculo">> => [] + , <<"vag1na">> => [] + , <<"vagina">> => [] + , <<"verdammt">> => [] + , <<"verga">> => [] + , <<"w1chsen">> => [] + , <<"wank">> => [] + , <<"wichsen">> => [] + , <<"x0ch0ta">> => [] + , <<"x0chota">> => [] + , <<"xana">> => [] + , <<"xoch0ta">> => [] + , <<"xochota">> => [] + , <<"z0cc01a">> => [] + , <<"z0cc0la">> => [] + , <<"z0cco1a">> => [] + , <<"z0ccola">> => [] + , <<"z1z1">> => [] + , <<"z1zi">> => [] + , <<"ziz1">> => [] + , <<"zizi">> => [] + , <<"zocc01a">> => [] + , <<"zocc0la">> => [] + , <<"zocco1a">> => [] + , <<"zoccola">> => [] + } . From e58a977220b5b2878fbf3a217eb1e11ad4e45d32 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 00:14:58 +0900 Subject: [PATCH 04/23] added constructor with unordered_set blocklist --- rebar.lock | 1 + src/sqids.erl | 137 +++++++++++++++++++++++++++++++++++++++- src/sqids_blocklist.erl | 2 +- 3 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 rebar.lock diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/src/sqids.erl b/src/sqids.erl index f9829e3..1575cc7 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -1,3 +1,138 @@ -module(sqids). --export([]). +-export([ + new/0 + , new/1 + , default_options/0 + ]). + +-export_type([ + options/0 + , str/0 + , blocklist/0 + , sqids/0 + ]). + +% String representations supported in this module is/are: +% - non multibyte binary format +-type str() :: unicode:latin1_binary(). + +-type char_() :: <<_:8>>. + +-type blocklist() :: sets:set(str()). + +-type options() :: #{ + alphabet => str() + , min_length => non_neg_integer() + , blocklist => blocklist() + }. + +-opaque sqids() :: #{ + alphabet := str() + , min_length := non_neg_integer() + , blocklist := blocklist() + }. + +-spec default_options() -> options(). +default_options() -> + #{ alphabet => + <<"abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "0123456789">> + , min_length => 0 + , blocklist => sqids_blocklist:get() + } . + +-spec new() -> sqids(). +new() -> + new(#{}). + +-spec new(options()) -> sqids(). +new(Options0) when is_map(Options0)-> + Options = maps:merge(default_options(), Options0), + case Options of + #{ alphabet := Alphabet + , min_length := MinLength + , blocklist := Blocklist + } when is_binary(Alphabet) + , is_integer(MinLength) + , MinLength >= 0 % no upper limit. + -> + case {sets:is_set(Blocklist), is_map(Blocklist)} of + {true, true} -> ok; + _ -> + erlang:error(badarg, [Options0]) + end; + _ -> + erlang:error(badarg, [Options0]) + end, + BinAlphabet = maps:get(alphabet, Options), + ListAlphabet = unicode:characters_to_list(BinAlphabet), + case {size(BinAlphabet), length(ListAlphabet)} of + {Size, Size} when Size < 3 -> + Reason0 = 'Alphabet length must be at least 3', + erlang:error(Reason0, [Options0]); + {Size, Size} -> + ok; + _ -> + Reason0 = 'Alphabet cannot contain multibyte characters', + erlang:error(Reason0, [Options0]) + end, + SetAlphabet = sets:from_list(ListAlphabet, [{version, 2}]), + case {size(BinAlphabet), sets:size(SetAlphabet)} of + {SetSize, SetSize} -> + ok; + _ -> + Reason1 = 'Alphabet must contain unique characters', + erlang:error(Reason1, [Options0]) + end, + AlphabetLowercased = string:casefold(BinAlphabet), + AlphabetCharSet = str_to_char_set(AlphabetLowercased), + FilteredBlocklist = maps:fold(fun + (Word, [], Acc) when size(Word) >= 3-> + WordLowercased = string:casefold(Word), + WordChars = str_to_char_list(WordLowercased), + try + lists:foreach(fun + (C) -> + case sets:is_element(C, AlphabetCharSet) of + true -> ok; + false -> throw({?MODULE, break}) + end + end, WordChars) + of + ok -> + Acc#{WordLowercased => []} + catch + throw:{?MODULE, break} -> + Acc + end; + (_, [], Acc) -> + Acc; + (_, _, _) -> + erlang:error(badarg, [Options0]) + end, #{}, maps:get(blocklist, Options)), + #{ alphabet => shuffle(BinAlphabet) + , min_length => maps:get(min_length, Options) + , blocklist => FilteredBlocklist + } ; +new(Options0) -> + erlang:error(badarg, [Options0]). + + +-spec shuffle(str()) -> str(). +shuffle(Alphabet) -> + % TODO + Alphabet. + +-spec str_to_char_set(str()) -> sets:set(char_()). +str_to_char_set(Str) -> + List = str_to_char_list(Str), + sets:from_list(List, [{version, 2}]). + +-spec str_to_char_list(str()) -> lists:list(char_()). +str_to_char_list(Str) -> + lists:map(fun(Char) -> + <> + end, binary_to_list(Str)). + diff --git a/src/sqids_blocklist.erl b/src/sqids_blocklist.erl index 9174ffc..1bf63ef 100644 --- a/src/sqids_blocklist.erl +++ b/src/sqids_blocklist.erl @@ -4,7 +4,7 @@ get/0 ]). --export_types([ +-export_type([ blocklist/0 ]). From 728398bbeaaa40d96a9114d5d2cefc2f50ebed94 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 00:34:03 +0900 Subject: [PATCH 05/23] allow version 1 set. --- src/sqids.erl | 11 +++++++++++ src/sqids_blocklist.erl | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sqids.erl b/src/sqids.erl index 1575cc7..dc41c42 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -48,6 +48,17 @@ new() -> new(#{}). -spec new(options()) -> sqids(). +new(#{blocklist:=Blocklist}=Opts) when not is_map(Blocklist) -> + % Erlang sets has version 1 and 2. + % Type sqids_blocklist:blocklist() is version 2. + % This converts version 1 to version 2. + SetVer2 = case sets:is_set(Blocklist) of + true -> + sets:from_list(sets:to_list(Blocklist), [{version, 2}]); + _ -> + erlang:error(badarg, [Opts]) + end, + new(Opts#{blocklist:=SetVer2}); new(Options0) when is_map(Options0)-> Options = maps:merge(default_options(), Options0), case Options of diff --git a/src/sqids_blocklist.erl b/src/sqids_blocklist.erl index 1bf63ef..ac20619 100644 --- a/src/sqids_blocklist.erl +++ b/src/sqids_blocklist.erl @@ -8,7 +8,7 @@ blocklist/0 ]). --type blocklist() :: sets:set(unicode:latin1_binary()). +-type blocklist() :: maps:map(unicode:latin1_binary(), nil()). -spec get() -> blocklist(). get() -> From 1420f0573d5ca15454d508df383b31895c72fad9 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 02:57:46 +0900 Subject: [PATCH 06/23] added encode/2 --- src/sqids.erl | 93 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/src/sqids.erl b/src/sqids.erl index dc41c42..97bbd1f 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -4,6 +4,7 @@ new/0 , new/1 , default_options/0 + , encode/2 ]). -export_type([ @@ -28,9 +29,11 @@ }. -opaque sqids() :: #{ - alphabet := str() + '?MODULE' := ?MODULE + , alphabet := str() , min_length := non_neg_integer() , blocklist := blocklist() + , n := non_neg_integer() }. -spec default_options() -> options(). @@ -41,7 +44,7 @@ default_options() -> "0123456789">> , min_length => 0 , blocklist => sqids_blocklist:get() - } . + }. -spec new() -> sqids(). new() -> @@ -123,19 +126,103 @@ new(Options0) when is_map(Options0)-> (_, _, _) -> erlang:error(badarg, [Options0]) end, #{}, maps:get(blocklist, Options)), - #{ alphabet => shuffle(BinAlphabet) + #{ '?MODULE' => ?MODULE + , alphabet => shuffle(BinAlphabet) , min_length => maps:get(min_length, Options) , blocklist => FilteredBlocklist + , n => size(BinAlphabet) } ; new(Options0) -> erlang:error(badarg, [Options0]). +-spec encode([non_neg_integer()], sqids()) -> str(). +encode([], #{'?MODULE':=?MODULE}) -> + <<>>; +encode(Numbers, Sqids=#{'?MODULE':=?MODULE}) -> + encode_numbers(Numbers, 0, Sqids); +encode(Arg1, Arg2) -> + erlang:error(badarg, [Arg1, Arg2]). + +-spec encode_numbers( + [non_neg_integer()], non_neg_integer(), sqids() + ) -> str(). +encode_numbers(Num, Inc, #{n:=N}=Sqids) when Inc > N -> + Reason = 'Reached max attempts to re-generate the ID', + erlang:error(Reason, [Num, Inc, Sqids]); +encode_numbers(Numbers, Increment, Sqids) -> + This = fun(Key) -> maps:get(Key, Sqids) end, + {_i, Offset0} = lists:foldl(fun(V, {I, A})-> + Next = binary:at(This(alphabet), V rem This(n)) + I + A, + {I+1, Next} + end, {0, length(Numbers)}, Numbers), + Offset1 = Offset0 rem This(n), + Offset = (Offset1 + Increment) rem This(n), + <> = This(alphabet), + Alphabet0 = <>, + Prefix = binary:at(Alphabet0, 0), + Alphabet1 = list_to_binary(lists:reverse(binary_to_list(Alphabet0))), + {RevCharList0, Alphabet2} = encode_input_array( + Numbers, [Prefix], Alphabet1 + ), + Id = case This(min_length) of + MinLength when MinLength > length(RevCharList0) -> + Separator = binary:at(Alphabet2, 0), + RevCharList1 = [Separator|RevCharList0], + Id0 = list_to_binary(lists:reverse(RevCharList1)), + id_padding(Id0, This(min_length), Alphabet2); + _ -> + list_to_binary(lists:reverse(RevCharList0)) + end, + case is_blocked_id(Id) of + false -> + Id; + _ -> + encode_numbers(Numbers, Increment+1, Sqids) + end. + +-spec encode_input_array( + [non_neg_integer(), ...], [non_neg_integer(), ...], str() + ) -> {[char_(), ...], str()}. +encode_input_array([Num], Id0, Alphabet) -> + <<_:1/binary, AlphabetWithoutSeparator/binary>> = Alphabet, + Id1 = [to_id(Num, AlphabetWithoutSeparator)|Id0], + {Id1, Alphabet}; +encode_input_array([Num|Numbers], Id0, Alphabet) -> + <> = Alphabet, + Id1 = [to_id(Num, AlphabetWithoutSeparator)|Id0], + Id2 = [Separator|Id1], + encode_input_array(Numbers, Id2, shuffle(Alphabet)). + +id_padding(Id0, MinLength, Alphabet0) when MinLength - size(Id0) > 0 -> + Alphabet = shuffle(Alphabet0), + Size = min(MinLength-size(Id0), size(Alphabet)), + <> = Alphabet, + Id = <>, + id_padding(Id, MinLength, Alphabet); +id_padding(Id, _MinLength, _Alphabet) -> + Id. + -spec shuffle(str()) -> str(). shuffle(Alphabet) -> % TODO Alphabet. +-spec to_id(non_neg_integer(), str()) -> char_(). +to_id(_Num, Alphabet) -> + % TODO + binary:at(Alphabet, 0). + +-spec to_number(str(), str()) -> non_neg_integer(). +to_number(_, _) -> + % TODO + 0. + +-spec is_blocked_id(str()) -> boolean(). +is_blocked_id(_) -> + % TODO + false. + -spec str_to_char_set(str()) -> sets:set(char_()). str_to_char_set(Str) -> List = str_to_char_list(Str), From 8c3a5fa3da481caee9b3549ec4e350d5f2b53c4d Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 03:12:43 +0900 Subject: [PATCH 07/23] validate non_neg_integer() --- src/sqids.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sqids.erl b/src/sqids.erl index 97bbd1f..46a8ddb 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -140,6 +140,12 @@ new(Options0) -> encode([], #{'?MODULE':=?MODULE}) -> <<>>; encode(Numbers, Sqids=#{'?MODULE':=?MODULE}) -> + lists:foreach(fun + (Num) when is_integer(Num) andalso Num >= 0 -> + ok; + (_) -> + erlang:error(badarg, [Numbers, Sqids]) + end, Numbers), encode_numbers(Numbers, 0, Sqids); encode(Arg1, Arg2) -> erlang:error(badarg, [Arg1, Arg2]). From c3f6c29c313da34a91a56d9260ee599a2b81c578 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 16:58:39 +0900 Subject: [PATCH 08/23] added decode/2 --- src/sqids.erl | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/sqids.erl b/src/sqids.erl index 46a8ddb..cd66733 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -5,6 +5,7 @@ , new/1 , default_options/0 , encode/2 + , decode/2 ]). -export_type([ @@ -209,6 +210,54 @@ id_padding(Id0, MinLength, Alphabet0) when MinLength - size(Id0) > 0 -> id_padding(Id, _MinLength, _Alphabet) -> Id. +-spec decode(str(), sqids()) -> [non_neg_integer()]. +decode(<<>>, #{'?MODULE':=?MODULE}) -> + []; +decode(Id, Sqids=#{'?MODULE':=?MODULE}) when is_binary(Id) -> + try + decode_(Id, Sqids) + of + Ret -> Ret + catch + {?MODULE, return, Ret} -> Ret + end; +decode(Arg1, Arg2) -> + erlang:error(badarg, [Arg1, Arg2]). + +decode_(Id0, Sqids) -> + This = fun(Key) -> maps:get(Key, Sqids) end, + lists:foreach(fun(C) -> + case sets:is_element(C, str_to_char_set(This(alphabet))) of + true -> ok; + _ -> throw({?MODULE, return, []}) + end + end, str_to_char_list(Id0)), + <> = Id0, + {Offset, _} = binary:match(This(alphabet), Prefix), + <> = This(alphabet), + Alphabet0 = <>, + Alphabet1 = list_to_binary(lists:reverse(binary_to_list(Alphabet0))), + decode_([Id1], Alphabet1, []). + +decode_([], _, Ret) -> + lists:reverse(Ret); +decode_([Id0], Alphabet0, Ret0) -> + <> = Alphabet0, + Chunks = binary:split(Id0, Separator), + case Chunks of + [<<>>|_] -> + lists:reverse(Ret0); + [Chunk|Id1] -> + Ret1 = [to_number(Chunk, AlphabetWithoutSeparator)|Ret0], + Alphabet1 = case length(Chunks) of + ChunksLength when ChunksLength > 1 -> + shuffle(Alphabet0); + _ -> + Alphabet0 + end, + decode_(Id1, Alphabet1, Ret1) + end. + -spec shuffle(str()) -> str(). shuffle(Alphabet) -> % TODO @@ -222,7 +271,7 @@ to_id(_Num, Alphabet) -> -spec to_number(str(), str()) -> non_neg_integer(). to_number(_, _) -> % TODO - 0. + 3. -spec is_blocked_id(str()) -> boolean(). is_blocked_id(_) -> From 652fdc012b1c01a902b8a6b3da95adb3ed854277 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 17:23:25 +0900 Subject: [PATCH 09/23] added specs for decode_ --- src/sqids.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sqids.erl b/src/sqids.erl index cd66733..d4b42fa 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -224,6 +224,7 @@ decode(Id, Sqids=#{'?MODULE':=?MODULE}) when is_binary(Id) -> decode(Arg1, Arg2) -> erlang:error(badarg, [Arg1, Arg2]). +-spec decode_(str(), sqids()) -> [non_neg_integer()]. decode_(Id0, Sqids) -> This = fun(Key) -> maps:get(Key, Sqids) end, lists:foreach(fun(C) -> @@ -239,6 +240,8 @@ decode_(Id0, Sqids) -> Alphabet1 = list_to_binary(lists:reverse(binary_to_list(Alphabet0))), decode_([Id1], Alphabet1, []). +-spec decode_(MaybeId, str(), sqids()) -> [non_neg_integer()] + when MaybeId :: nil() | nonempty_list(str()). % [] or [<<...>>] decode_([], _, Ret) -> lists:reverse(Ret); decode_([Id0], Alphabet0, Ret0) -> From 55502231bcc46b02ed6199aae0648a167db938d8 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 18:35:32 +0900 Subject: [PATCH 10/23] implement shuffle/1 --- src/sqids.erl | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/sqids.erl b/src/sqids.erl index d4b42fa..aeeac24 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -264,7 +264,44 @@ decode_([Id0], Alphabet0, Ret0) -> -spec shuffle(str()) -> str(). shuffle(Alphabet) -> % TODO - Alphabet. + shuffle_(0, size(Alphabet)-1, Alphabet). + +-spec shuffle_(non_neg_integer(), non_neg_integer(), str()) -> str(). +shuffle_(_, 0, Alphabet) -> + Alphabet; +shuffle_(I, J, Alphabet) -> + N = size(Alphabet), + R = (I * J + binary:at(Alphabet, I) + binary:at(Alphabet, J)) rem N, + shuffle_(I+1, J-1, swap(I, R, Alphabet)). + +-spec swap(non_neg_integer(), non_neg_integer(), str()) -> str(). +swap(X, X, BinaryString) -> + BinaryString; +swap(Y, X, BinaryString) when Y > X -> + swap(X, Y, BinaryString); +swap(X, Y, BinaryString) when ( + is_integer(X) andalso X >= 0 andalso + is_integer(Y) andalso Y >= 0 andalso + is_binary(BinaryString) + ) -> + PrefixSize = X, + LeftSize = 1, + InterfixSize = Y - X -1, + RightSize = 1, + << Prefix:PrefixSize/binary + , Left:LeftSize/binary + , Interfix:InterfixSize/binary + , Right:RightSize/binary + , Suffix/binary + >> = BinaryString, + << Prefix/binary + , Right/binary + , Interfix/binary + , Left/binary + , Suffix/binary + >>; +swap(Arg1, Arg2, Arg3) -> + erlang:error(badarg, [Arg1, Arg2, Arg3]). -spec to_id(non_neg_integer(), str()) -> char_(). to_id(_Num, Alphabet) -> From 8b08b8a5c3d3bf09964c062c99a844f65ce6ffe3 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 19:22:15 +0900 Subject: [PATCH 11/23] implement to_id/2 --- src/sqids.erl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sqids.erl b/src/sqids.erl index aeeac24..36ae679 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -263,7 +263,6 @@ decode_([Id0], Alphabet0, Ret0) -> -spec shuffle(str()) -> str(). shuffle(Alphabet) -> - % TODO shuffle_(0, size(Alphabet)-1, Alphabet). -spec shuffle_(non_neg_integer(), non_neg_integer(), str()) -> str(). @@ -304,9 +303,17 @@ swap(Arg1, Arg2, Arg3) -> erlang:error(badarg, [Arg1, Arg2, Arg3]). -spec to_id(non_neg_integer(), str()) -> char_(). -to_id(_Num, Alphabet) -> - % TODO - binary:at(Alphabet, 0). +to_id(Num, Alphabet) -> + to_id_(Num, Alphabet, <<>>). + +-spec to_id_(non_neg_integer(), str(), str()) -> str(). +to_id_(0, _, Id) when size(Id) > 0 -> + Id; +to_id_(Num0, Alphabet, Id0) -> + Char = binary:at(Alphabet, Num0 rem size(Alphabet)), + Id1 = <>, + Num1 = Num0 div size(Alphabet), + to_id_(Num1, Alphabet, Id1). -spec to_number(str(), str()) -> non_neg_integer(). to_number(_, _) -> From 5a0bd5a0ff8fe3e4c2b6306430aa30d88746cd22 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 20:03:37 +0900 Subject: [PATCH 12/23] implement to_number/2 --- src/sqids.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sqids.erl b/src/sqids.erl index 36ae679..64f1bb6 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -316,9 +316,11 @@ to_id_(Num0, Alphabet, Id0) -> to_id_(Num1, Alphabet, Id1). -spec to_number(str(), str()) -> non_neg_integer(). -to_number(_, _) -> - % TODO - 3. +to_number(Id, Alphabet) -> + lists:foldl(fun(V, A) -> + {Index, _} = binary:match(Alphabet, <>), + A * size(Alphabet) + Index + end, 0, binary_to_list(Id)). -spec is_blocked_id(str()) -> boolean(). is_blocked_id(_) -> From ec5623170ef6eaa3d5c1d2b4307b613247fe627b Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 20:08:35 +0900 Subject: [PATCH 13/23] fix some mistakes --- src/sqids.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sqids.erl b/src/sqids.erl index 64f1bb6..97bf464 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -181,7 +181,7 @@ encode_numbers(Numbers, Increment, Sqids) -> _ -> list_to_binary(lists:reverse(RevCharList0)) end, - case is_blocked_id(Id) of + case is_blocked_id(Id, This(blocklist)) of false -> Id; _ -> @@ -311,19 +311,19 @@ to_id_(0, _, Id) when size(Id) > 0 -> Id; to_id_(Num0, Alphabet, Id0) -> Char = binary:at(Alphabet, Num0 rem size(Alphabet)), - Id1 = <>, + Id1 = <>, Num1 = Num0 div size(Alphabet), to_id_(Num1, Alphabet, Id1). -spec to_number(str(), str()) -> non_neg_integer(). to_number(Id, Alphabet) -> lists:foldl(fun(V, A) -> - {Index, _} = binary:match(Alphabet, <>), + {Index, _} = binary:match(Alphabet, <>), A * size(Alphabet) + Index end, 0, binary_to_list(Id)). --spec is_blocked_id(str()) -> boolean(). -is_blocked_id(_) -> +-spec is_blocked_id(str(), blocklist()) -> boolean(). +is_blocked_id(_, _) -> % TODO false. From 0d1a0752f75e5ccc6352284a3aecc72d029ffe61 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 21:16:51 +0900 Subject: [PATCH 14/23] implement is_blocked_id/2 exported function only for debug --- src/sqids.erl | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/sqids.erl b/src/sqids.erl index 97bf464..0274a14 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -6,6 +6,7 @@ , default_options/0 , encode/2 , decode/2 + , is_blocked_id/2 ]). -export_type([ @@ -323,9 +324,42 @@ to_number(Id, Alphabet) -> end, 0, binary_to_list(Id)). -spec is_blocked_id(str(), blocklist()) -> boolean(). -is_blocked_id(_, _) -> - % TODO - false. +is_blocked_id(Id0, Blocklist) -> + Id = string:casefold(Id0), + try + maps:foreach(fun(Word, []) -> + case is_blocked_id_(Word, Id) of + true -> throw({?MODULE, return, true}); + false -> ok + end + end, Blocklist) + of + _ -> + false + catch + throw:{?MODULE, return, true} -> + true + end. + +-spec is_blocked_id_(Word::str(), Id::str()) -> boolean(). +is_blocked_id_(Word, Id) when size(Word) =< 3 orelse size(Id) =< 3 -> + (Word =:= Id); +is_blocked_id_(Word, Id) -> + case re:run(<<"ab1c">>, <<"\\d">>) of + nomatch -> + case binary:match(Id, Word) of + nomatch -> false; + _ -> true + end; + _ -> + RevWord = reverse_str(Word), + W = size(Word), + case {Id, reverse_str(Id)} of + {<>, _} -> true; + {_, <>} -> true; + _ -> false + end + end. -spec str_to_char_set(str()) -> sets:set(char_()). str_to_char_set(Str) -> @@ -338,3 +372,12 @@ str_to_char_list(Str) -> <> end, binary_to_list(Str)). +-spec reverse_str(str()) -> str(). +reverse_str(Str) -> + reverse_str_(Str, <<>>). + +-spec reverse_str_(str(), str()) -> str(). +reverse_str_(<<>>, Str) -> Str; +reverse_str_(<>, Str) -> + reverse_str_(Tail, <>). + From 8d2bba1e50d8cbc89f11a76b66fb655f6817a090 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 21:35:50 +0900 Subject: [PATCH 15/23] change blocklist to iovec like type --- src/sqids.erl | 41 +- src/sqids_blocklist.erl | 1124 +++++++++++++++++++-------------------- 2 files changed, 573 insertions(+), 592 deletions(-) diff --git a/src/sqids.erl b/src/sqids.erl index 0274a14..8cdc81d 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -6,7 +6,6 @@ , default_options/0 , encode/2 , decode/2 - , is_blocked_id/2 ]). -export_type([ @@ -22,7 +21,7 @@ -type char_() :: <<_:8>>. --type blocklist() :: sets:set(str()). +-type blocklist() :: lists:list(str()). -type options() :: #{ alphabet => str() @@ -53,17 +52,6 @@ new() -> new(#{}). -spec new(options()) -> sqids(). -new(#{blocklist:=Blocklist}=Opts) when not is_map(Blocklist) -> - % Erlang sets has version 1 and 2. - % Type sqids_blocklist:blocklist() is version 2. - % This converts version 1 to version 2. - SetVer2 = case sets:is_set(Blocklist) of - true -> - sets:from_list(sets:to_list(Blocklist), [{version, 2}]); - _ -> - erlang:error(badarg, [Opts]) - end, - new(Opts#{blocklist:=SetVer2}); new(Options0) when is_map(Options0)-> Options = maps:merge(default_options(), Options0), case Options of @@ -73,12 +61,7 @@ new(Options0) when is_map(Options0)-> } when is_binary(Alphabet) , is_integer(MinLength) , MinLength >= 0 % no upper limit. - -> - case {sets:is_set(Blocklist), is_map(Blocklist)} of - {true, true} -> ok; - _ -> - erlang:error(badarg, [Options0]) - end; + , is_list(Blocklist) -> ok; _ -> erlang:error(badarg, [Options0]) end, @@ -104,8 +87,8 @@ new(Options0) when is_map(Options0)-> end, AlphabetLowercased = string:casefold(BinAlphabet), AlphabetCharSet = str_to_char_set(AlphabetLowercased), - FilteredBlocklist = maps:fold(fun - (Word, [], Acc) when size(Word) >= 3-> + FilteredBlocklist = lists:filtermap(fun + (Word) when size(Word) >= 3-> WordLowercased = string:casefold(Word), WordChars = str_to_char_list(WordLowercased), try @@ -118,16 +101,14 @@ new(Options0) when is_map(Options0)-> end, WordChars) of ok -> - Acc#{WordLowercased => []} + {true, WordLowercased} catch throw:{?MODULE, break} -> - Acc + false end; - (_, [], Acc) -> - Acc; - (_, _, _) -> - erlang:error(badarg, [Options0]) - end, #{}, maps:get(blocklist, Options)), + (_) -> + false + end, maps:get(blocklist, Options)), #{ '?MODULE' => ?MODULE , alphabet => shuffle(BinAlphabet) , min_length => maps:get(min_length, Options) @@ -327,7 +308,7 @@ to_number(Id, Alphabet) -> is_blocked_id(Id0, Blocklist) -> Id = string:casefold(Id0), try - maps:foreach(fun(Word, []) -> + lists:foreach(fun(Word) -> case is_blocked_id_(Word, Id) of true -> throw({?MODULE, return, true}); false -> ok @@ -345,7 +326,7 @@ is_blocked_id(Id0, Blocklist) -> is_blocked_id_(Word, Id) when size(Word) =< 3 orelse size(Id) =< 3 -> (Word =:= Id); is_blocked_id_(Word, Id) -> - case re:run(<<"ab1c">>, <<"\\d">>) of + case re:run(Id, <<"\\d">>) of nomatch -> case binary:match(Id, Word) of nomatch -> false; diff --git a/src/sqids_blocklist.erl b/src/sqids_blocklist.erl index ac20619..b5c05b4 100644 --- a/src/sqids_blocklist.erl +++ b/src/sqids_blocklist.erl @@ -8,568 +8,568 @@ blocklist/0 ]). --type blocklist() :: maps:map(unicode:latin1_binary(), nil()). +-type blocklist() :: lists:list(unicode:latin1_binary()). -spec get() -> blocklist(). get() -> - #{ <<"0rgasm">> => [] - , <<"1d10t">> => [] - , <<"1d1ot">> => [] - , <<"1di0t">> => [] - , <<"1diot">> => [] - , <<"1eccacu10">> => [] - , <<"1eccacu1o">> => [] - , <<"1eccacul0">> => [] - , <<"1eccaculo">> => [] - , <<"1mbec11e">> => [] - , <<"1mbec1le">> => [] - , <<"1mbeci1e">> => [] - , <<"1mbecile">> => [] - , <<"a11upat0">> => [] - , <<"a11upato">> => [] - , <<"a1lupat0">> => [] - , <<"a1lupato">> => [] - , <<"aand">> => [] - , <<"ah01e">> => [] - , <<"ah0le">> => [] - , <<"aho1e">> => [] - , <<"ahole">> => [] - , <<"al1upat0">> => [] - , <<"al1upato">> => [] - , <<"allupat0">> => [] - , <<"allupato">> => [] - , <<"ana1">> => [] - , <<"ana1e">> => [] - , <<"anal">> => [] - , <<"anale">> => [] - , <<"anus">> => [] - , <<"arrapat0">> => [] - , <<"arrapato">> => [] - , <<"arsch">> => [] - , <<"arse">> => [] - , <<"ass">> => [] - , <<"b00b">> => [] - , <<"b00be">> => [] - , <<"b01ata">> => [] - , <<"b0ceta">> => [] - , <<"b0iata">> => [] - , <<"b0ob">> => [] - , <<"b0obe">> => [] - , <<"b0sta">> => [] - , <<"b1tch">> => [] - , <<"b1te">> => [] - , <<"b1tte">> => [] - , <<"ba1atkar">> => [] - , <<"balatkar">> => [] - , <<"bastard0">> => [] - , <<"bastardo">> => [] - , <<"batt0na">> => [] - , <<"battona">> => [] - , <<"bitch">> => [] - , <<"bite">> => [] - , <<"bitte">> => [] - , <<"bo0b">> => [] - , <<"bo0be">> => [] - , <<"bo1ata">> => [] - , <<"boceta">> => [] - , <<"boiata">> => [] - , <<"boob">> => [] - , <<"boobe">> => [] - , <<"bosta">> => [] - , <<"bran1age">> => [] - , <<"bran1er">> => [] - , <<"bran1ette">> => [] - , <<"bran1eur">> => [] - , <<"bran1euse">> => [] - , <<"branlage">> => [] - , <<"branler">> => [] - , <<"branlette">> => [] - , <<"branleur">> => [] - , <<"branleuse">> => [] - , <<"c0ck">> => [] - , <<"c0g110ne">> => [] - , <<"c0g11one">> => [] - , <<"c0g1i0ne">> => [] - , <<"c0g1ione">> => [] - , <<"c0gl10ne">> => [] - , <<"c0gl1one">> => [] - , <<"c0gli0ne">> => [] - , <<"c0glione">> => [] - , <<"c0na">> => [] - , <<"c0nnard">> => [] - , <<"c0nnasse">> => [] - , <<"c0nne">> => [] - , <<"c0u111es">> => [] - , <<"c0u11les">> => [] - , <<"c0u1l1es">> => [] - , <<"c0u1lles">> => [] - , <<"c0ui11es">> => [] - , <<"c0ui1les">> => [] - , <<"c0uil1es">> => [] - , <<"c0uilles">> => [] - , <<"c11t">> => [] - , <<"c11t0">> => [] - , <<"c11to">> => [] - , <<"c1it">> => [] - , <<"c1it0">> => [] - , <<"c1ito">> => [] - , <<"cabr0n">> => [] - , <<"cabra0">> => [] - , <<"cabrao">> => [] - , <<"cabron">> => [] - , <<"caca">> => [] - , <<"cacca">> => [] - , <<"cacete">> => [] - , <<"cagante">> => [] - , <<"cagar">> => [] - , <<"cagare">> => [] - , <<"cagna">> => [] - , <<"cara1h0">> => [] - , <<"cara1ho">> => [] - , <<"caracu10">> => [] - , <<"caracu1o">> => [] - , <<"caracul0">> => [] - , <<"caraculo">> => [] - , <<"caralh0">> => [] - , <<"caralho">> => [] - , <<"cazz0">> => [] - , <<"cazz1mma">> => [] - , <<"cazzata">> => [] - , <<"cazzimma">> => [] - , <<"cazzo">> => [] - , <<"ch00t1a">> => [] - , <<"ch00t1ya">> => [] - , <<"ch00tia">> => [] - , <<"ch00tiya">> => [] - , <<"ch0d">> => [] - , <<"ch0ot1a">> => [] - , <<"ch0ot1ya">> => [] - , <<"ch0otia">> => [] - , <<"ch0otiya">> => [] - , <<"ch1asse">> => [] - , <<"ch1avata">> => [] - , <<"ch1er">> => [] - , <<"ch1ng0">> => [] - , <<"ch1ngadaz0s">> => [] - , <<"ch1ngadazos">> => [] - , <<"ch1ngader1ta">> => [] - , <<"ch1ngaderita">> => [] - , <<"ch1ngar">> => [] - , <<"ch1ngo">> => [] - , <<"ch1ngues">> => [] - , <<"ch1nk">> => [] - , <<"chatte">> => [] - , <<"chiasse">> => [] - , <<"chiavata">> => [] - , <<"chier">> => [] - , <<"ching0">> => [] - , <<"chingadaz0s">> => [] - , <<"chingadazos">> => [] - , <<"chingader1ta">> => [] - , <<"chingaderita">> => [] - , <<"chingar">> => [] - , <<"chingo">> => [] - , <<"chingues">> => [] - , <<"chink">> => [] - , <<"cho0t1a">> => [] - , <<"cho0t1ya">> => [] - , <<"cho0tia">> => [] - , <<"cho0tiya">> => [] - , <<"chod">> => [] - , <<"choot1a">> => [] - , <<"choot1ya">> => [] - , <<"chootia">> => [] - , <<"chootiya">> => [] - , <<"cl1t">> => [] - , <<"cl1t0">> => [] - , <<"cl1to">> => [] - , <<"clit">> => [] - , <<"clit0">> => [] - , <<"clito">> => [] - , <<"cock">> => [] - , <<"cog110ne">> => [] - , <<"cog11one">> => [] - , <<"cog1i0ne">> => [] - , <<"cog1ione">> => [] - , <<"cogl10ne">> => [] - , <<"cogl1one">> => [] - , <<"cogli0ne">> => [] - , <<"coglione">> => [] - , <<"cona">> => [] - , <<"connard">> => [] - , <<"connasse">> => [] - , <<"conne">> => [] - , <<"cou111es">> => [] - , <<"cou11les">> => [] - , <<"cou1l1es">> => [] - , <<"cou1lles">> => [] - , <<"coui11es">> => [] - , <<"coui1les">> => [] - , <<"couil1es">> => [] - , <<"couilles">> => [] - , <<"cracker">> => [] - , <<"crap">> => [] - , <<"cu10">> => [] - , <<"cu1att0ne">> => [] - , <<"cu1attone">> => [] - , <<"cu1er0">> => [] - , <<"cu1ero">> => [] - , <<"cu1o">> => [] - , <<"cul0">> => [] - , <<"culatt0ne">> => [] - , <<"culattone">> => [] - , <<"culer0">> => [] - , <<"culero">> => [] - , <<"culo">> => [] - , <<"cum">> => [] - , <<"cunt">> => [] - , <<"d11d0">> => [] - , <<"d11do">> => [] - , <<"d1ck">> => [] - , <<"d1ld0">> => [] - , <<"d1ldo">> => [] - , <<"damn">> => [] - , <<"de1ch">> => [] - , <<"deich">> => [] - , <<"depp">> => [] - , <<"di1d0">> => [] - , <<"di1do">> => [] - , <<"dick">> => [] - , <<"dild0">> => [] - , <<"dildo">> => [] - , <<"dyke">> => [] - , <<"encu1e">> => [] - , <<"encule">> => [] - , <<"enema">> => [] - , <<"enf01re">> => [] - , <<"enf0ire">> => [] - , <<"enfo1re">> => [] - , <<"enfoire">> => [] - , <<"estup1d0">> => [] - , <<"estup1do">> => [] - , <<"estupid0">> => [] - , <<"estupido">> => [] - , <<"etr0n">> => [] - , <<"etron">> => [] - , <<"f0da">> => [] - , <<"f0der">> => [] - , <<"f0ttere">> => [] - , <<"f0tters1">> => [] - , <<"f0ttersi">> => [] - , <<"f0tze">> => [] - , <<"f0utre">> => [] - , <<"f1ca">> => [] - , <<"f1cker">> => [] - , <<"f1ga">> => [] - , <<"fag">> => [] - , <<"fica">> => [] - , <<"ficker">> => [] - , <<"figa">> => [] - , <<"foda">> => [] - , <<"foder">> => [] - , <<"fottere">> => [] - , <<"fotters1">> => [] - , <<"fottersi">> => [] - , <<"fotze">> => [] - , <<"foutre">> => [] - , <<"fr0c10">> => [] - , <<"fr0c1o">> => [] - , <<"fr0ci0">> => [] - , <<"fr0cio">> => [] - , <<"fr0sc10">> => [] - , <<"fr0sc1o">> => [] - , <<"fr0sci0">> => [] - , <<"fr0scio">> => [] - , <<"froc10">> => [] - , <<"froc1o">> => [] - , <<"froci0">> => [] - , <<"frocio">> => [] - , <<"frosc10">> => [] - , <<"frosc1o">> => [] - , <<"frosci0">> => [] - , <<"froscio">> => [] - , <<"fuck">> => [] - , <<"g00">> => [] - , <<"g0o">> => [] - , <<"g0u1ne">> => [] - , <<"g0uine">> => [] - , <<"gandu">> => [] - , <<"go0">> => [] - , <<"goo">> => [] - , <<"gou1ne">> => [] - , <<"gouine">> => [] - , <<"gr0gnasse">> => [] - , <<"grognasse">> => [] - , <<"haram1">> => [] - , <<"harami">> => [] - , <<"haramzade">> => [] - , <<"hund1n">> => [] - , <<"hundin">> => [] - , <<"id10t">> => [] - , <<"id1ot">> => [] - , <<"idi0t">> => [] - , <<"idiot">> => [] - , <<"imbec11e">> => [] - , <<"imbec1le">> => [] - , <<"imbeci1e">> => [] - , <<"imbecile">> => [] - , <<"j1zz">> => [] - , <<"jerk">> => [] - , <<"jizz">> => [] - , <<"k1ke">> => [] - , <<"kam1ne">> => [] - , <<"kamine">> => [] - , <<"kike">> => [] - , <<"leccacu10">> => [] - , <<"leccacu1o">> => [] - , <<"leccacul0">> => [] - , <<"leccaculo">> => [] - , <<"m1erda">> => [] - , <<"m1gn0tta">> => [] - , <<"m1gnotta">> => [] - , <<"m1nch1a">> => [] - , <<"m1nchia">> => [] - , <<"m1st">> => [] - , <<"mam0n">> => [] - , <<"mamahuev0">> => [] - , <<"mamahuevo">> => [] - , <<"mamon">> => [] - , <<"masturbat10n">> => [] - , <<"masturbat1on">> => [] - , <<"masturbate">> => [] - , <<"masturbati0n">> => [] - , <<"masturbation">> => [] - , <<"merd0s0">> => [] - , <<"merd0so">> => [] - , <<"merda">> => [] - , <<"merde">> => [] - , <<"merdos0">> => [] - , <<"merdoso">> => [] - , <<"mierda">> => [] - , <<"mign0tta">> => [] - , <<"mignotta">> => [] - , <<"minch1a">> => [] - , <<"minchia">> => [] - , <<"mist">> => [] - , <<"musch1">> => [] - , <<"muschi">> => [] - , <<"n1gger">> => [] - , <<"neger">> => [] - , <<"negr0">> => [] - , <<"negre">> => [] - , <<"negro">> => [] - , <<"nerch1a">> => [] - , <<"nerchia">> => [] - , <<"nigger">> => [] - , <<"orgasm">> => [] - , <<"p00p">> => [] - , <<"p011a">> => [] - , <<"p01la">> => [] - , <<"p0l1a">> => [] - , <<"p0lla">> => [] - , <<"p0mp1n0">> => [] - , <<"p0mp1no">> => [] - , <<"p0mpin0">> => [] - , <<"p0mpino">> => [] - , <<"p0op">> => [] - , <<"p0rca">> => [] - , <<"p0rn">> => [] - , <<"p0rra">> => [] - , <<"p0uff1asse">> => [] - , <<"p0uffiasse">> => [] - , <<"p1p1">> => [] - , <<"p1pi">> => [] - , <<"p1r1a">> => [] - , <<"p1rla">> => [] - , <<"p1sc10">> => [] - , <<"p1sc1o">> => [] - , <<"p1sci0">> => [] - , <<"p1scio">> => [] - , <<"p1sser">> => [] - , <<"pa11e">> => [] - , <<"pa1le">> => [] - , <<"pal1e">> => [] - , <<"palle">> => [] - , <<"pane1e1r0">> => [] - , <<"pane1e1ro">> => [] - , <<"pane1eir0">> => [] - , <<"pane1eiro">> => [] - , <<"panele1r0">> => [] - , <<"panele1ro">> => [] - , <<"paneleir0">> => [] - , <<"paneleiro">> => [] - , <<"patakha">> => [] - , <<"pec0r1na">> => [] - , <<"pec0rina">> => [] - , <<"pecor1na">> => [] - , <<"pecorina">> => [] - , <<"pen1s">> => [] - , <<"pendej0">> => [] - , <<"pendejo">> => [] - , <<"penis">> => [] - , <<"pip1">> => [] - , <<"pipi">> => [] - , <<"pir1a">> => [] - , <<"pirla">> => [] - , <<"pisc10">> => [] - , <<"pisc1o">> => [] - , <<"pisci0">> => [] - , <<"piscio">> => [] - , <<"pisser">> => [] - , <<"po0p">> => [] - , <<"po11a">> => [] - , <<"po1la">> => [] - , <<"pol1a">> => [] - , <<"polla">> => [] - , <<"pomp1n0">> => [] - , <<"pomp1no">> => [] - , <<"pompin0">> => [] - , <<"pompino">> => [] - , <<"poop">> => [] - , <<"porca">> => [] - , <<"porn">> => [] - , <<"porra">> => [] - , <<"pouff1asse">> => [] - , <<"pouffiasse">> => [] - , <<"pr1ck">> => [] - , <<"prick">> => [] - , <<"pussy">> => [] - , <<"put1za">> => [] - , <<"puta">> => [] - , <<"puta1n">> => [] - , <<"putain">> => [] - , <<"pute">> => [] - , <<"putiza">> => [] - , <<"puttana">> => [] - , <<"queca">> => [] - , <<"r0mp1ba11e">> => [] - , <<"r0mp1ba1le">> => [] - , <<"r0mp1bal1e">> => [] - , <<"r0mp1balle">> => [] - , <<"r0mpiba11e">> => [] - , <<"r0mpiba1le">> => [] - , <<"r0mpibal1e">> => [] - , <<"r0mpiballe">> => [] - , <<"rand1">> => [] - , <<"randi">> => [] - , <<"rape">> => [] - , <<"recch10ne">> => [] - , <<"recch1one">> => [] - , <<"recchi0ne">> => [] - , <<"recchione">> => [] - , <<"retard">> => [] - , <<"romp1ba11e">> => [] - , <<"romp1ba1le">> => [] - , <<"romp1bal1e">> => [] - , <<"romp1balle">> => [] - , <<"rompiba11e">> => [] - , <<"rompiba1le">> => [] - , <<"rompibal1e">> => [] - , <<"rompiballe">> => [] - , <<"ruff1an0">> => [] - , <<"ruff1ano">> => [] - , <<"ruffian0">> => [] - , <<"ruffiano">> => [] - , <<"s1ut">> => [] - , <<"sa10pe">> => [] - , <<"sa1aud">> => [] - , <<"sa1ope">> => [] - , <<"sacanagem">> => [] - , <<"sal0pe">> => [] - , <<"salaud">> => [] - , <<"salope">> => [] - , <<"saugnapf">> => [] - , <<"sb0rr0ne">> => [] - , <<"sb0rra">> => [] - , <<"sb0rrone">> => [] - , <<"sbattere">> => [] - , <<"sbatters1">> => [] - , <<"sbattersi">> => [] - , <<"sborr0ne">> => [] - , <<"sborra">> => [] - , <<"sborrone">> => [] - , <<"sc0pare">> => [] - , <<"sc0pata">> => [] - , <<"sch1ampe">> => [] - , <<"sche1se">> => [] - , <<"sche1sse">> => [] - , <<"scheise">> => [] - , <<"scheisse">> => [] - , <<"schlampe">> => [] - , <<"schwachs1nn1g">> => [] - , <<"schwachs1nnig">> => [] - , <<"schwachsinn1g">> => [] - , <<"schwachsinnig">> => [] - , <<"schwanz">> => [] - , <<"scopare">> => [] - , <<"scopata">> => [] - , <<"sexy">> => [] - , <<"sh1t">> => [] - , <<"shit">> => [] - , <<"slut">> => [] - , <<"sp0mp1nare">> => [] - , <<"sp0mpinare">> => [] - , <<"spomp1nare">> => [] - , <<"spompinare">> => [] - , <<"str0nz0">> => [] - , <<"str0nza">> => [] - , <<"str0nzo">> => [] - , <<"stronz0">> => [] - , <<"stronza">> => [] - , <<"stronzo">> => [] - , <<"stup1d">> => [] - , <<"stupid">> => [] - , <<"succh1am1">> => [] - , <<"succh1ami">> => [] - , <<"succhiam1">> => [] - , <<"succhiami">> => [] - , <<"sucker">> => [] - , <<"t0pa">> => [] - , <<"tapette">> => [] - , <<"test1c1e">> => [] - , <<"test1cle">> => [] - , <<"testic1e">> => [] - , <<"testicle">> => [] - , <<"tette">> => [] - , <<"topa">> => [] - , <<"tr01a">> => [] - , <<"tr0ia">> => [] - , <<"tr0mbare">> => [] - , <<"tr1ng1er">> => [] - , <<"tr1ngler">> => [] - , <<"tring1er">> => [] - , <<"tringler">> => [] - , <<"tro1a">> => [] - , <<"troia">> => [] - , <<"trombare">> => [] - , <<"turd">> => [] - , <<"twat">> => [] - , <<"vaffancu10">> => [] - , <<"vaffancu1o">> => [] - , <<"vaffancul0">> => [] - , <<"vaffanculo">> => [] - , <<"vag1na">> => [] - , <<"vagina">> => [] - , <<"verdammt">> => [] - , <<"verga">> => [] - , <<"w1chsen">> => [] - , <<"wank">> => [] - , <<"wichsen">> => [] - , <<"x0ch0ta">> => [] - , <<"x0chota">> => [] - , <<"xana">> => [] - , <<"xoch0ta">> => [] - , <<"xochota">> => [] - , <<"z0cc01a">> => [] - , <<"z0cc0la">> => [] - , <<"z0cco1a">> => [] - , <<"z0ccola">> => [] - , <<"z1z1">> => [] - , <<"z1zi">> => [] - , <<"ziz1">> => [] - , <<"zizi">> => [] - , <<"zocc01a">> => [] - , <<"zocc0la">> => [] - , <<"zocco1a">> => [] - , <<"zoccola">> => [] - } . + [ <<"0rgasm">> + , <<"1d10t">> + , <<"1d1ot">> + , <<"1di0t">> + , <<"1diot">> + , <<"1eccacu10">> + , <<"1eccacu1o">> + , <<"1eccacul0">> + , <<"1eccaculo">> + , <<"1mbec11e">> + , <<"1mbec1le">> + , <<"1mbeci1e">> + , <<"1mbecile">> + , <<"a11upat0">> + , <<"a11upato">> + , <<"a1lupat0">> + , <<"a1lupato">> + , <<"aand">> + , <<"ah01e">> + , <<"ah0le">> + , <<"aho1e">> + , <<"ahole">> + , <<"al1upat0">> + , <<"al1upato">> + , <<"allupat0">> + , <<"allupato">> + , <<"ana1">> + , <<"ana1e">> + , <<"anal">> + , <<"anale">> + , <<"anus">> + , <<"arrapat0">> + , <<"arrapato">> + , <<"arsch">> + , <<"arse">> + , <<"ass">> + , <<"b00b">> + , <<"b00be">> + , <<"b01ata">> + , <<"b0ceta">> + , <<"b0iata">> + , <<"b0ob">> + , <<"b0obe">> + , <<"b0sta">> + , <<"b1tch">> + , <<"b1te">> + , <<"b1tte">> + , <<"ba1atkar">> + , <<"balatkar">> + , <<"bastard0">> + , <<"bastardo">> + , <<"batt0na">> + , <<"battona">> + , <<"bitch">> + , <<"bite">> + , <<"bitte">> + , <<"bo0b">> + , <<"bo0be">> + , <<"bo1ata">> + , <<"boceta">> + , <<"boiata">> + , <<"boob">> + , <<"boobe">> + , <<"bosta">> + , <<"bran1age">> + , <<"bran1er">> + , <<"bran1ette">> + , <<"bran1eur">> + , <<"bran1euse">> + , <<"branlage">> + , <<"branler">> + , <<"branlette">> + , <<"branleur">> + , <<"branleuse">> + , <<"c0ck">> + , <<"c0g110ne">> + , <<"c0g11one">> + , <<"c0g1i0ne">> + , <<"c0g1ione">> + , <<"c0gl10ne">> + , <<"c0gl1one">> + , <<"c0gli0ne">> + , <<"c0glione">> + , <<"c0na">> + , <<"c0nnard">> + , <<"c0nnasse">> + , <<"c0nne">> + , <<"c0u111es">> + , <<"c0u11les">> + , <<"c0u1l1es">> + , <<"c0u1lles">> + , <<"c0ui11es">> + , <<"c0ui1les">> + , <<"c0uil1es">> + , <<"c0uilles">> + , <<"c11t">> + , <<"c11t0">> + , <<"c11to">> + , <<"c1it">> + , <<"c1it0">> + , <<"c1ito">> + , <<"cabr0n">> + , <<"cabra0">> + , <<"cabrao">> + , <<"cabron">> + , <<"caca">> + , <<"cacca">> + , <<"cacete">> + , <<"cagante">> + , <<"cagar">> + , <<"cagare">> + , <<"cagna">> + , <<"cara1h0">> + , <<"cara1ho">> + , <<"caracu10">> + , <<"caracu1o">> + , <<"caracul0">> + , <<"caraculo">> + , <<"caralh0">> + , <<"caralho">> + , <<"cazz0">> + , <<"cazz1mma">> + , <<"cazzata">> + , <<"cazzimma">> + , <<"cazzo">> + , <<"ch00t1a">> + , <<"ch00t1ya">> + , <<"ch00tia">> + , <<"ch00tiya">> + , <<"ch0d">> + , <<"ch0ot1a">> + , <<"ch0ot1ya">> + , <<"ch0otia">> + , <<"ch0otiya">> + , <<"ch1asse">> + , <<"ch1avata">> + , <<"ch1er">> + , <<"ch1ng0">> + , <<"ch1ngadaz0s">> + , <<"ch1ngadazos">> + , <<"ch1ngader1ta">> + , <<"ch1ngaderita">> + , <<"ch1ngar">> + , <<"ch1ngo">> + , <<"ch1ngues">> + , <<"ch1nk">> + , <<"chatte">> + , <<"chiasse">> + , <<"chiavata">> + , <<"chier">> + , <<"ching0">> + , <<"chingadaz0s">> + , <<"chingadazos">> + , <<"chingader1ta">> + , <<"chingaderita">> + , <<"chingar">> + , <<"chingo">> + , <<"chingues">> + , <<"chink">> + , <<"cho0t1a">> + , <<"cho0t1ya">> + , <<"cho0tia">> + , <<"cho0tiya">> + , <<"chod">> + , <<"choot1a">> + , <<"choot1ya">> + , <<"chootia">> + , <<"chootiya">> + , <<"cl1t">> + , <<"cl1t0">> + , <<"cl1to">> + , <<"clit">> + , <<"clit0">> + , <<"clito">> + , <<"cock">> + , <<"cog110ne">> + , <<"cog11one">> + , <<"cog1i0ne">> + , <<"cog1ione">> + , <<"cogl10ne">> + , <<"cogl1one">> + , <<"cogli0ne">> + , <<"coglione">> + , <<"cona">> + , <<"connard">> + , <<"connasse">> + , <<"conne">> + , <<"cou111es">> + , <<"cou11les">> + , <<"cou1l1es">> + , <<"cou1lles">> + , <<"coui11es">> + , <<"coui1les">> + , <<"couil1es">> + , <<"couilles">> + , <<"cracker">> + , <<"crap">> + , <<"cu10">> + , <<"cu1att0ne">> + , <<"cu1attone">> + , <<"cu1er0">> + , <<"cu1ero">> + , <<"cu1o">> + , <<"cul0">> + , <<"culatt0ne">> + , <<"culattone">> + , <<"culer0">> + , <<"culero">> + , <<"culo">> + , <<"cum">> + , <<"cunt">> + , <<"d11d0">> + , <<"d11do">> + , <<"d1ck">> + , <<"d1ld0">> + , <<"d1ldo">> + , <<"damn">> + , <<"de1ch">> + , <<"deich">> + , <<"depp">> + , <<"di1d0">> + , <<"di1do">> + , <<"dick">> + , <<"dild0">> + , <<"dildo">> + , <<"dyke">> + , <<"encu1e">> + , <<"encule">> + , <<"enema">> + , <<"enf01re">> + , <<"enf0ire">> + , <<"enfo1re">> + , <<"enfoire">> + , <<"estup1d0">> + , <<"estup1do">> + , <<"estupid0">> + , <<"estupido">> + , <<"etr0n">> + , <<"etron">> + , <<"f0da">> + , <<"f0der">> + , <<"f0ttere">> + , <<"f0tters1">> + , <<"f0ttersi">> + , <<"f0tze">> + , <<"f0utre">> + , <<"f1ca">> + , <<"f1cker">> + , <<"f1ga">> + , <<"fag">> + , <<"fica">> + , <<"ficker">> + , <<"figa">> + , <<"foda">> + , <<"foder">> + , <<"fottere">> + , <<"fotters1">> + , <<"fottersi">> + , <<"fotze">> + , <<"foutre">> + , <<"fr0c10">> + , <<"fr0c1o">> + , <<"fr0ci0">> + , <<"fr0cio">> + , <<"fr0sc10">> + , <<"fr0sc1o">> + , <<"fr0sci0">> + , <<"fr0scio">> + , <<"froc10">> + , <<"froc1o">> + , <<"froci0">> + , <<"frocio">> + , <<"frosc10">> + , <<"frosc1o">> + , <<"frosci0">> + , <<"froscio">> + , <<"fuck">> + , <<"g00">> + , <<"g0o">> + , <<"g0u1ne">> + , <<"g0uine">> + , <<"gandu">> + , <<"go0">> + , <<"goo">> + , <<"gou1ne">> + , <<"gouine">> + , <<"gr0gnasse">> + , <<"grognasse">> + , <<"haram1">> + , <<"harami">> + , <<"haramzade">> + , <<"hund1n">> + , <<"hundin">> + , <<"id10t">> + , <<"id1ot">> + , <<"idi0t">> + , <<"idiot">> + , <<"imbec11e">> + , <<"imbec1le">> + , <<"imbeci1e">> + , <<"imbecile">> + , <<"j1zz">> + , <<"jerk">> + , <<"jizz">> + , <<"k1ke">> + , <<"kam1ne">> + , <<"kamine">> + , <<"kike">> + , <<"leccacu10">> + , <<"leccacu1o">> + , <<"leccacul0">> + , <<"leccaculo">> + , <<"m1erda">> + , <<"m1gn0tta">> + , <<"m1gnotta">> + , <<"m1nch1a">> + , <<"m1nchia">> + , <<"m1st">> + , <<"mam0n">> + , <<"mamahuev0">> + , <<"mamahuevo">> + , <<"mamon">> + , <<"masturbat10n">> + , <<"masturbat1on">> + , <<"masturbate">> + , <<"masturbati0n">> + , <<"masturbation">> + , <<"merd0s0">> + , <<"merd0so">> + , <<"merda">> + , <<"merde">> + , <<"merdos0">> + , <<"merdoso">> + , <<"mierda">> + , <<"mign0tta">> + , <<"mignotta">> + , <<"minch1a">> + , <<"minchia">> + , <<"mist">> + , <<"musch1">> + , <<"muschi">> + , <<"n1gger">> + , <<"neger">> + , <<"negr0">> + , <<"negre">> + , <<"negro">> + , <<"nerch1a">> + , <<"nerchia">> + , <<"nigger">> + , <<"orgasm">> + , <<"p00p">> + , <<"p011a">> + , <<"p01la">> + , <<"p0l1a">> + , <<"p0lla">> + , <<"p0mp1n0">> + , <<"p0mp1no">> + , <<"p0mpin0">> + , <<"p0mpino">> + , <<"p0op">> + , <<"p0rca">> + , <<"p0rn">> + , <<"p0rra">> + , <<"p0uff1asse">> + , <<"p0uffiasse">> + , <<"p1p1">> + , <<"p1pi">> + , <<"p1r1a">> + , <<"p1rla">> + , <<"p1sc10">> + , <<"p1sc1o">> + , <<"p1sci0">> + , <<"p1scio">> + , <<"p1sser">> + , <<"pa11e">> + , <<"pa1le">> + , <<"pal1e">> + , <<"palle">> + , <<"pane1e1r0">> + , <<"pane1e1ro">> + , <<"pane1eir0">> + , <<"pane1eiro">> + , <<"panele1r0">> + , <<"panele1ro">> + , <<"paneleir0">> + , <<"paneleiro">> + , <<"patakha">> + , <<"pec0r1na">> + , <<"pec0rina">> + , <<"pecor1na">> + , <<"pecorina">> + , <<"pen1s">> + , <<"pendej0">> + , <<"pendejo">> + , <<"penis">> + , <<"pip1">> + , <<"pipi">> + , <<"pir1a">> + , <<"pirla">> + , <<"pisc10">> + , <<"pisc1o">> + , <<"pisci0">> + , <<"piscio">> + , <<"pisser">> + , <<"po0p">> + , <<"po11a">> + , <<"po1la">> + , <<"pol1a">> + , <<"polla">> + , <<"pomp1n0">> + , <<"pomp1no">> + , <<"pompin0">> + , <<"pompino">> + , <<"poop">> + , <<"porca">> + , <<"porn">> + , <<"porra">> + , <<"pouff1asse">> + , <<"pouffiasse">> + , <<"pr1ck">> + , <<"prick">> + , <<"pussy">> + , <<"put1za">> + , <<"puta">> + , <<"puta1n">> + , <<"putain">> + , <<"pute">> + , <<"putiza">> + , <<"puttana">> + , <<"queca">> + , <<"r0mp1ba11e">> + , <<"r0mp1ba1le">> + , <<"r0mp1bal1e">> + , <<"r0mp1balle">> + , <<"r0mpiba11e">> + , <<"r0mpiba1le">> + , <<"r0mpibal1e">> + , <<"r0mpiballe">> + , <<"rand1">> + , <<"randi">> + , <<"rape">> + , <<"recch10ne">> + , <<"recch1one">> + , <<"recchi0ne">> + , <<"recchione">> + , <<"retard">> + , <<"romp1ba11e">> + , <<"romp1ba1le">> + , <<"romp1bal1e">> + , <<"romp1balle">> + , <<"rompiba11e">> + , <<"rompiba1le">> + , <<"rompibal1e">> + , <<"rompiballe">> + , <<"ruff1an0">> + , <<"ruff1ano">> + , <<"ruffian0">> + , <<"ruffiano">> + , <<"s1ut">> + , <<"sa10pe">> + , <<"sa1aud">> + , <<"sa1ope">> + , <<"sacanagem">> + , <<"sal0pe">> + , <<"salaud">> + , <<"salope">> + , <<"saugnapf">> + , <<"sb0rr0ne">> + , <<"sb0rra">> + , <<"sb0rrone">> + , <<"sbattere">> + , <<"sbatters1">> + , <<"sbattersi">> + , <<"sborr0ne">> + , <<"sborra">> + , <<"sborrone">> + , <<"sc0pare">> + , <<"sc0pata">> + , <<"sch1ampe">> + , <<"sche1se">> + , <<"sche1sse">> + , <<"scheise">> + , <<"scheisse">> + , <<"schlampe">> + , <<"schwachs1nn1g">> + , <<"schwachs1nnig">> + , <<"schwachsinn1g">> + , <<"schwachsinnig">> + , <<"schwanz">> + , <<"scopare">> + , <<"scopata">> + , <<"sexy">> + , <<"sh1t">> + , <<"shit">> + , <<"slut">> + , <<"sp0mp1nare">> + , <<"sp0mpinare">> + , <<"spomp1nare">> + , <<"spompinare">> + , <<"str0nz0">> + , <<"str0nza">> + , <<"str0nzo">> + , <<"stronz0">> + , <<"stronza">> + , <<"stronzo">> + , <<"stup1d">> + , <<"stupid">> + , <<"succh1am1">> + , <<"succh1ami">> + , <<"succhiam1">> + , <<"succhiami">> + , <<"sucker">> + , <<"t0pa">> + , <<"tapette">> + , <<"test1c1e">> + , <<"test1cle">> + , <<"testic1e">> + , <<"testicle">> + , <<"tette">> + , <<"topa">> + , <<"tr01a">> + , <<"tr0ia">> + , <<"tr0mbare">> + , <<"tr1ng1er">> + , <<"tr1ngler">> + , <<"tring1er">> + , <<"tringler">> + , <<"tro1a">> + , <<"troia">> + , <<"trombare">> + , <<"turd">> + , <<"twat">> + , <<"vaffancu10">> + , <<"vaffancu1o">> + , <<"vaffancul0">> + , <<"vaffanculo">> + , <<"vag1na">> + , <<"vagina">> + , <<"verdammt">> + , <<"verga">> + , <<"w1chsen">> + , <<"wank">> + , <<"wichsen">> + , <<"x0ch0ta">> + , <<"x0chota">> + , <<"xana">> + , <<"xoch0ta">> + , <<"xochota">> + , <<"z0cc01a">> + , <<"z0cc0la">> + , <<"z0cco1a">> + , <<"z0ccola">> + , <<"z1z1">> + , <<"z1zi">> + , <<"ziz1">> + , <<"zizi">> + , <<"zocc01a">> + , <<"zocc0la">> + , <<"zocco1a">> + , <<"zoccola">> + ] . From 30f3caf1dc6198ecf5c6acfa7d5fa9d2f17f596d Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sat, 20 Jan 2024 23:54:58 +0900 Subject: [PATCH 16/23] fix bug --- src/sqids.erl | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/sqids.erl b/src/sqids.erl index 8cdc81d..d8a80db 100644 --- a/src/sqids.erl +++ b/src/sqids.erl @@ -66,18 +66,20 @@ new(Options0) when is_map(Options0)-> erlang:error(badarg, [Options0]) end, BinAlphabet = maps:get(alphabet, Options), - ListAlphabet = unicode:characters_to_list(BinAlphabet), - case {size(BinAlphabet), length(ListAlphabet)} of - {Size, Size} when Size < 3 -> + ListAlphabet = binary_to_list(BinAlphabet), + Is7Bit = unicode:bin_is_7bit(BinAlphabet), + case {size(BinAlphabet), Is7Bit} of + {Size, true} when Size < 3 -> Reason0 = 'Alphabet length must be at least 3', erlang:error(Reason0, [Options0]); - {Size, Size} -> + {_Size, true} -> ok; _ -> Reason0 = 'Alphabet cannot contain multibyte characters', erlang:error(Reason0, [Options0]) end, - SetAlphabet = sets:from_list(ListAlphabet, [{version, 2}]), + SetsOpt = [{version, 2}], + SetAlphabet = sets:from_list(ListAlphabet, SetsOpt), case {size(BinAlphabet), sets:size(SetAlphabet)} of {SetSize, SetSize} -> ok; @@ -109,10 +111,13 @@ new(Options0) when is_map(Options0)-> (_) -> false end, maps:get(blocklist, Options)), + UniqueFilteredBlocklist = sets:to_list( + sets:from_list(FilteredBlocklist, SetsOpt) + ), #{ '?MODULE' => ?MODULE , alphabet => shuffle(BinAlphabet) , min_length => maps:get(min_length, Options) - , blocklist => FilteredBlocklist + , blocklist => UniqueFilteredBlocklist , n => size(BinAlphabet) } ; new(Options0) -> @@ -326,7 +331,7 @@ is_blocked_id(Id0, Blocklist) -> is_blocked_id_(Word, Id) when size(Word) =< 3 orelse size(Id) =< 3 -> (Word =:= Id); is_blocked_id_(Word, Id) -> - case re:run(Id, <<"\\d">>) of + case re:run(Word, <<"\\d">>) of nomatch -> case binary:match(Id, Word) of nomatch -> false; From c95a31b2ae130f0e59eb9411bb2b9f59245af15f Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sun, 21 Jan 2024 15:46:44 +0900 Subject: [PATCH 17/23] Add tests --- test/sqids_alphabet_tests.erl | 58 +++++++++++++++ test/sqids_blocklist_tests.erl | 103 +++++++++++++++++++++++++++ test/sqids_encoding_tests.erl | 125 +++++++++++++++++++++++++++++++++ test/sqids_minlength_tests.erl | 86 +++++++++++++++++++++++ 4 files changed, 372 insertions(+) create mode 100644 test/sqids_alphabet_tests.erl create mode 100644 test/sqids_blocklist_tests.erl create mode 100644 test/sqids_encoding_tests.erl create mode 100644 test/sqids_minlength_tests.erl diff --git a/test/sqids_alphabet_tests.erl b/test/sqids_alphabet_tests.erl new file mode 100644 index 0000000..6571b41 --- /dev/null +++ b/test/sqids_alphabet_tests.erl @@ -0,0 +1,58 @@ +-module(sqids_alphabet_tests). +-include_lib("eunit/include/eunit.hrl"). +-import(sqids, [new/0, new/1, encode/2, decode/2]). + +-compile([export_all]). + +simple_test() -> + Sqids = new(#{ + alphabet => <<"0123456789abcdef">> + }), + Numbers = [1, 2, 3], + Id = <<"489158">>, + ?assertEqual(Id, encode(Numbers, Sqids)), + ?assertEqual(Numbers, decode(Id, Sqids)), + ok. + +short_alphabet_test() -> + Sqids = new(#{ + alphabet => <<"abc">> + }), + Numbers = [1, 2, 3], + ?assertEqual(Numbers, decode(encode(Numbers, Sqids), Sqids)), + ok. + +long_alphabet_test() -> + Sqids = new(#{ + alphabet => + << "abcdefghijklmnopqrstuvwxyz" + , "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + , "0123456789!@#$%^&*()-_+|{}" + , "[];:\\'\"/?.>,<`~" + >> + }), + Numbers = [1, 2, 3], + ?assertEqual(Numbers, decode(encode(Numbers, Sqids), Sqids)), + ok. + +multibyte_characters_test() -> + Reason = 'Alphabet cannot contain multibyte characters', + ?assertError(Reason, new(#{ + alphabet => <<"ë1092">> + })), + ok. + +repeating_alphabet_characters_test() -> + Reason = 'Alphabet must contain unique characters', + ?assertError(Reason, new(#{ + alphabet => <<"aabcdefg">> + })), + ok. + +too_short_of_an_alphabet_test() -> + Reason = 'Alphabet length must be at least 3', + ?assertError(Reason, new(#{ + alphabet => <<"ab">> + })), + ok. + diff --git a/test/sqids_blocklist_tests.erl b/test/sqids_blocklist_tests.erl new file mode 100644 index 0000000..f5b3ea1 --- /dev/null +++ b/test/sqids_blocklist_tests.erl @@ -0,0 +1,103 @@ +-module(sqids_blocklist_tests). +-include_lib("eunit/include/eunit.hrl"). +-import(sqids, [new/0, new/1, encode/2, decode/2]). + +-compile([export_all]). + +'if no custom blocklist param, use the default blocklist _test' () -> + Sqids = new(), + ?assertEqual([4572721], decode(<<"aho1e">>, Sqids)), + ?assertEqual(<<"JExTR">>, encode([4572721], Sqids)), + ok. + +'if an empty blocklist param passed, don\'t use any blocklist _test' () -> + Sqids = new(#{ + blocklist => [] + }), + ?assertEqual([4572721], decode(<<"aho1e">>, Sqids)), + ?assertEqual(<<"aho1e">>, encode([4572721], Sqids)), + ok. + +'if a non-empty blocklist param passed, use only that _test' () -> + Sqids = new(#{ + blocklist => [ + <<"ArUO">> % originally encoded [100000] + ] + }), + + % make sure we don't use the default blocklist + ?assertEqual([4572721], decode(<<"aho1e">>, Sqids)), + ?assertEqual(<<"aho1e">>, encode([4572721], Sqids)), + + % make sure we are using the passed blocklist + ?assertEqual([100000], decode(<<"ArUO">>, Sqids)), + ?assertEqual(<<"QyG4">>, encode([100000], Sqids)), + ?assertEqual([100000], decode(<<"QyG4">>, Sqids)), + ok. + +blocklist_test() -> + Sqids = new(#{ + blocklist => [ + <<"JSwXFaosAN">> % normal result of 1st encoding, let's block that word on purpose + , <<"OCjV9JK64o">> % result of 2nd encoding + , <<"rBHf">> % result of 3rd encoding is `4rBHfOiqd3`, let's block a substring + , <<"79SM">> % result of 4th encoding is `dyhgw479SM`, let's block the postfix + , <<"7tE6">> % result of 4th encoding is `7tE6jdAHLe`, let's block the prefix + ] + }), + ?assertEqual(<<"1aYeB7bRUt">>, encode([1_000_000, 2_000_000], Sqids)), + ?assertEqual([1_000_000, 2_000_000], decode(<<"1aYeB7bRUt">>, Sqids)), + ok. + +'decoding blocklist words should still work _test' () -> + Sqids = new(#{ + blocklist => [ + <<"86Rf07">> + , <<"se8ojk">> + , <<"ARsz1p">> + , <<"Q8AI49">> + , <<"5sQRZO">> + ] + }), + ?assertEqual([1, 2, 3], decode(<<"86Rf07">>, Sqids)), + ?assertEqual([1, 2, 3], decode(<<"se8ojk">>, Sqids)), + ?assertEqual([1, 2, 3], decode(<<"ARsz1p">>, Sqids)), + ?assertEqual([1, 2, 3], decode(<<"Q8AI49">>, Sqids)), + ?assertEqual([1, 2, 3], decode(<<"5sQRZO">>, Sqids)), + ok. + +'match against a short blocklist word _test' () -> + Sqids = new(#{ + blocklist => [<<"pnd">>] + }), + ?assertEqual([1000], decode(encode([1000], Sqids), Sqids)), + ok. + +'blocklist filtering in constructor _test' () -> + Sqids = new(#{ + alphabet => <<"ABCDEFGHIJKLMNOPQRSTUVWXYZ">> + % lowercase blocklist in only-uppercase alphabet + , blocklist => [<<"sxnzkl">>] + }), + Id = encode([1, 2, 3], Sqids), + Numbers = decode(Id, Sqids), + % without blocklist, would've been "SXNZKL" + ?assertEqual(<<"IBSHOZ">>, Id), + ?assertEqual([1, 2, 3], Numbers), + ok. + +'max encoding attempts _test' () -> + Alphabet = <<"abc">>, + MinLength = 3, + Blocklist = [<<"cab">>, <<"abc">>, <<"bca">>], + Sqids = new(#{ + alphabet => Alphabet + , min_length => MinLength + , blocklist => Blocklist + }), + ?assertEqual(MinLength, size(Alphabet)), + ?assertEqual(MinLength, length(Blocklist)), + Reason = 'Reached max attempts to re-generate the ID', + ?assertError(Reason, encode([0], Sqids)), + ok. + diff --git a/test/sqids_encoding_tests.erl b/test/sqids_encoding_tests.erl new file mode 100644 index 0000000..2a6a78e --- /dev/null +++ b/test/sqids_encoding_tests.erl @@ -0,0 +1,125 @@ +-module(sqids_encoding_tests). +-include_lib("eunit/include/eunit.hrl"). +-import(sqids, [new/0, new/1, encode/2, decode/2]). + +-compile([export_all]). + +simple_test() -> + Sqids = new(), + Numbers = [1, 2, 3], + Id = <<"86Rf07">>, + ?assertEqual(Id, encode(Numbers, Sqids)), + ?assertEqual(Numbers, decode(Id, Sqids)), + ok. + +different_inputs_test() -> + Sqids = new(), + LargeNum = round(1.0e+308) * round(1.0e+308), + Numbers = [0, 0, 0, 1, 2, 3, 100, 1_000, 100_000, 1_000_000, LargeNum], + assert(Sqids, Numbers), + ok. + +incremental_numbers_test() -> + Sqids = new(), + assert(Sqids, <<"bM">>, [0]), + assert(Sqids, <<"Uk">>, [1]), + assert(Sqids, <<"gb">>, [2]), + assert(Sqids, <<"Ef">>, [3]), + assert(Sqids, <<"Vq">>, [4]), + assert(Sqids, <<"uw">>, [5]), + assert(Sqids, <<"OI">>, [6]), + assert(Sqids, <<"AX">>, [7]), + assert(Sqids, <<"p6">>, [8]), + assert(Sqids, <<"nJ">>, [9]), + ok. + +'incremental numbers, same index 0 _test' () -> + Sqids = new(), + assert(Sqids, <<"SvIz">>, [0, 0]), + assert(Sqids, <<"n3qa">>, [0, 1]), + assert(Sqids, <<"tryF">>, [0, 2]), + assert(Sqids, <<"eg6q">>, [0, 3]), + assert(Sqids, <<"rSCF">>, [0, 4]), + assert(Sqids, <<"sR8x">>, [0, 5]), + assert(Sqids, <<"uY2M">>, [0, 6]), + assert(Sqids, <<"74dI">>, [0, 7]), + assert(Sqids, <<"30WX">>, [0, 8]), + assert(Sqids, <<"moxr">>, [0, 9]), + ok. + +'incremental numbers, same index 1 _test' () -> + Sqids = new(), + assert(Sqids, <<"SvIz">>, [0, 0]), + assert(Sqids, <<"nWqP">>, [1, 0]), + assert(Sqids, <<"tSyw">>, [2, 0]), + assert(Sqids, <<"eX68">>, [3, 0]), + assert(Sqids, <<"rxCY">>, [4, 0]), + assert(Sqids, <<"sV8a">>, [5, 0]), + assert(Sqids, <<"uf2K">>, [6, 0]), + assert(Sqids, <<"7Cdk">>, [7, 0]), + assert(Sqids, <<"3aWP">>, [8, 0]), + assert(Sqids, <<"m2xn">>, [9, 0]), + ok. + +multi_input_test() -> + Sqids = new(), + Numbers = lists:seq(0, 99), + ?assertEqual(0, lists:nth(1, Numbers)), + ?assertEqual(99, lists:last(Numbers)), + assert(Sqids, Numbers), + ok. + +'encoding no numbers _test' () -> + Sqids = new(), + ?assertEqual(<<"">>, encode([], Sqids)), + ok. + +'decoding empty string _test' () -> + Sqids = new(), + ?assertEqual([], decode(<<"*">>, Sqids)), + ok. + +'decoding an ID with an invalid character _test' () -> + Sqids = new(), + ?assertEqual([], decode(<<"*">>, Sqids)), + ok. + + +%%% internal functions %%% +assert(Arg1, Sqids) when is_map(Sqids) -> + assert(Sqids, Arg1); +assert(Sqids, Numbers) when is_list(Numbers) -> + ?assertEqual(Numbers, decode(encode(Numbers, Sqids), Sqids)), + ok; +assert(Sqids, Id) when is_binary(Id) -> + ?assertEqual(Id, encode(decode(Id, Sqids), Sqids)), + ok. + +assert(Arg1, Sqids, Arg2) when is_map(Sqids) -> + assert(Sqids, Arg1, Arg2); +assert(Arg1, Arg2, Sqids) when is_map(Sqids) -> + assert(Sqids, Arg1, Arg2); +assert(Sqids, Numbers, Id) when is_list(Numbers), is_binary(Id) -> + assert(Sqids, Id, Numbers); +assert(Sqids, Id, Numbers) -> + ?assertEqual(Numbers, decode(Id, Sqids)), + ?assertEqual(Id, encode(Numbers, Sqids)), + ok. + +assert_functions_test() -> + Sqids = new(), + Numbers = [1, 2, 3], + Id = <<"86Rf07">>, + % assert function args can be in any order. + assert(Sqids, Numbers), + assert(Sqids, Id), + assert(Numbers, Sqids), + assert(Id, Sqids), + assert(Sqids, Id, Numbers), + assert(Sqids, Numbers, Id), + assert(Id, Sqids, Numbers), + assert(Numbers, Sqids, Id), + assert(Id, Numbers, Sqids), + assert(Numbers, Id, Sqids), + ok. + diff --git a/test/sqids_minlength_tests.erl b/test/sqids_minlength_tests.erl new file mode 100644 index 0000000..d64bd25 --- /dev/null +++ b/test/sqids_minlength_tests.erl @@ -0,0 +1,86 @@ +-module(sqids_minlength_tests). +-include_lib("eunit/include/eunit.hrl"). +-import(sqids, [new/0, new/1, encode/2, decode/2]). + +-compile([export_all]). + +simple_test() -> + Sqids = new(#{ + min_length => size(default_options(alphabet)) + }), + Numbers = [1, 2, 3], + Id = <<"86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTM">>, + ?assertEqual(Id, encode(Numbers, Sqids)), + ?assertEqual(Numbers, decode(Id, Sqids)), + ok. + +incremental_test() -> + Numbers = [1, 2, 3], + Size = size(default_options(alphabet)), + Map = #{ + 6 => <<"86Rf07">> + , 7 => <<"86Rf07x">> + , 8 => <<"86Rf07xd">> + , 9 => <<"86Rf07xd4">> + , 10 => <<"86Rf07xd4z">> + , 11 => <<"86Rf07xd4zB">> + , 12 => <<"86Rf07xd4zBm">> + , 13 => <<"86Rf07xd4zBmi">> + , Size + 0 => <<"86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTM">> + , Size + 1 => <<"86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMy">> + , Size + 2 => <<"86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMyf">> + , Size + 3 => <<"86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMyf1">> + }, + maps:foreach(fun(MinLength, Id) -> + Sqids = new(#{min_length=>MinLength}), + ?assertEqual(Id, encode(Numbers, Sqids)), + ?assertEqual(MinLength, size(encode(Numbers, Sqids))), + ?assertEqual(Numbers, decode(Id, Sqids)) + end, Map), + ok. + +incremental_numbers_test() -> + Sqids = new(#{ + min_length => size(default_options(alphabet)) + }), + Ids = #{ + <<"SvIzsqYMyQwI3GWgJAe17URxX8V924Co0DaTZLtFjHriEn5bPhcSkfmvOslpBu">> => [0, 0] + , <<"n3qafPOLKdfHpuNw3M61r95svbeJGk7aAEgYn4WlSjXURmF8IDqZBy0CT2VxQc">> => [0, 1] + , <<"tryFJbWcFMiYPg8sASm51uIV93GXTnvRzyfLleh06CpodJD42B7OraKtkQNxUZ">> => [0, 2] + , <<"eg6ql0A3XmvPoCzMlB6DraNGcWSIy5VR8iYup2Qk4tjZFKe1hbwfgHdUTsnLqE">> => [0, 3] + , <<"rSCFlp0rB2inEljaRdxKt7FkIbODSf8wYgTsZM1HL9JzN35cyoqueUvVWCm4hX">> => [0, 4] + , <<"sR8xjC8WQkOwo74PnglH1YFdTI0eaf56RGVSitzbjuZ3shNUXBrqLxEJyAmKv2">> => [0, 5] + , <<"uY2MYFqCLpgx5XQcjdtZK286AwWV7IBGEfuS9yTmbJvkzoUPeYRHr4iDs3naN0">> => [0, 6] + , <<"74dID7X28VLQhBlnGmjZrec5wTA1fqpWtK4YkaoEIM9SRNiC3gUJH0OFvsPDdy">> => [0, 7] + , <<"30WXpesPhgKiEI5RHTY7xbB1GnytJvXOl2p0AcUjdF6waZDo9Qk8VLzMuWrqCS">> => [0, 8] + , <<"moxr3HqLAK0GsTND6jowfZz3SUx7cQ8aC54Pl1RbIvFXmEJuBMYVeW9yrdOtin">> => [0, 9] + }, + maps:foreach(fun(Id, Numbers) -> + ?assertEqual(Id, encode(Numbers, Sqids)), + ?assertEqual(Numbers, decode(Id, Sqids)) + end, Ids), + ok. + +min_lengths_test() -> + LargeNum = round(1.0e+308) * round(1.0e+308), + MinLengthList = [0, 1, 5, 10, size(default_options(alphabet))], + NumbersList = [ + [0] + , [0, 0, 0, 0, 0] + , [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + , [100, 200, 300] + , [1_000, 2_000, 3_000] + , [1_000_000] + , [LargeNum] + ], + lists:foreach(fun(MinLength)->lists:foreach(fun(Numbers)-> + Sqids = new(#{min_length=>MinLength}), + Id = encode(Numbers, Sqids), + ?assert(size(Id) >= MinLength), + ?assertEqual(Numbers, decode(Id, Sqids)) + end, NumbersList)end, MinLengthList), + ok. + +default_options(Key) -> + maps:get(Key, sqids:default_options()). + From 979e733766d7a2888310c0ea6eb4652440c42f32 Mon Sep 17 00:00:00 2001 From: ts-klassen <104614062+ts-klassen@users.noreply.github.com> Date: Sun, 21 Jan 2024 15:51:49 +0900 Subject: [PATCH 18/23] Add Github Actions --- .github/workflows/erlang.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/erlang.yml diff --git a/.github/workflows/erlang.yml b/.github/workflows/erlang.yml new file mode 100644 index 0000000..b2eef23 --- /dev/null +++ b/.github/workflows/erlang.yml @@ -0,0 +1,26 @@ +name: Erlang CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + + build: + + runs-on: ubuntu-latest + + container: + image: erlang:22.0.7 + + steps: + - uses: actions/checkout@v3 + - name: Compile + run: rebar3 compile + - name: Run tests + run: rebar3 do eunit, ct From 55acb3217fb23790d7920fa8ae914035610ffb53 Mon Sep 17 00:00:00 2001 From: ts-klassen <104614062+ts-klassen@users.noreply.github.com> Date: Sun, 21 Jan 2024 15:55:37 +0900 Subject: [PATCH 19/23] Test for Erlang/OTP 24 Update and rename erlang.yml to erlang24.yml --- .github/workflows/{erlang.yml => erlang24.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{erlang.yml => erlang24.yml} (92%) diff --git a/.github/workflows/erlang.yml b/.github/workflows/erlang24.yml similarity index 92% rename from .github/workflows/erlang.yml rename to .github/workflows/erlang24.yml index b2eef23..7eacdf7 100644 --- a/.github/workflows/erlang.yml +++ b/.github/workflows/erlang24.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest container: - image: erlang:22.0.7 + image: erlang:24 steps: - uses: actions/checkout@v3 From 88ee3dba3131b0140fd07b380f210cf53ab77cda Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sun, 21 Jan 2024 16:00:09 +0900 Subject: [PATCH 20/23] Test for Erlang/OTP 25 --- .github/workflows/erlang24.yml | 2 +- .github/workflows/erlang25.yml | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/erlang25.yml diff --git a/.github/workflows/erlang24.yml b/.github/workflows/erlang24.yml index 7eacdf7..fb9d2ca 100644 --- a/.github/workflows/erlang24.yml +++ b/.github/workflows/erlang24.yml @@ -1,4 +1,4 @@ -name: Erlang CI +name: Erlang/OTP 24 CI on: push: diff --git a/.github/workflows/erlang25.yml b/.github/workflows/erlang25.yml new file mode 100644 index 0000000..a7a01b2 --- /dev/null +++ b/.github/workflows/erlang25.yml @@ -0,0 +1,26 @@ +name: Erlang/OTP 25 CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + + build: + + runs-on: ubuntu-latest + + container: + image: erlang:25 + + steps: + - uses: actions/checkout@v3 + - name: Compile + run: rebar3 compile + - name: Run tests + run: rebar3 do eunit, ct From a40d57fba4208165828bb683bcac786000f47d19 Mon Sep 17 00:00:00 2001 From: ts-klassen Date: Sun, 21 Jan 2024 16:05:55 +0900 Subject: [PATCH 21/23] Test for Erlang/OTP 26 --- .github/workflows/erlang.yml | 54 ++++++++++++++++++++++++++++++++++ .github/workflows/erlang24.yml | 26 ---------------- .github/workflows/erlang25.yml | 26 ---------------- 3 files changed, 54 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/erlang.yml delete mode 100644 .github/workflows/erlang24.yml delete mode 100644 .github/workflows/erlang25.yml diff --git a/.github/workflows/erlang.yml b/.github/workflows/erlang.yml new file mode 100644 index 0000000..2819851 --- /dev/null +++ b/.github/workflows/erlang.yml @@ -0,0 +1,54 @@ +name: Erlang CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + + erlang24: + + runs-on: ubuntu-latest + + container: + image: erlang:24 + + steps: + - uses: actions/checkout@v3 + - name: Compile + run: rebar3 compile + - name: Run tests + run: rebar3 do eunit, ct + + erlang25: + + runs-on: ubuntu-latest + + container: + image: erlang:25 + + steps: + - uses: actions/checkout@v3 + - name: Compile + run: rebar3 compile + - name: Run tests + run: rebar3 do eunit, ct + + erlang26: + + runs-on: ubuntu-latest + + container: + image: erlang:26 + + steps: + - uses: actions/checkout@v3 + - name: Compile + run: rebar3 compile + - name: Run tests + run: rebar3 do eunit, ct diff --git a/.github/workflows/erlang24.yml b/.github/workflows/erlang24.yml deleted file mode 100644 index fb9d2ca..0000000 --- a/.github/workflows/erlang24.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Erlang/OTP 24 CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -permissions: - contents: read - -jobs: - - build: - - runs-on: ubuntu-latest - - container: - image: erlang:24 - - steps: - - uses: actions/checkout@v3 - - name: Compile - run: rebar3 compile - - name: Run tests - run: rebar3 do eunit, ct diff --git a/.github/workflows/erlang25.yml b/.github/workflows/erlang25.yml deleted file mode 100644 index a7a01b2..0000000 --- a/.github/workflows/erlang25.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Erlang/OTP 25 CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -permissions: - contents: read - -jobs: - - build: - - runs-on: ubuntu-latest - - container: - image: erlang:25 - - steps: - - uses: actions/checkout@v3 - - name: Compile - run: rebar3 compile - - name: Run tests - run: rebar3 do eunit, ct From 88f86717771f71d26b3a68a158376e50df214a8e Mon Sep 17 00:00:00 2001 From: ts-klassen <104614062+ts-klassen@users.noreply.github.com> Date: Sun, 21 Jan 2024 16:30:11 +0900 Subject: [PATCH 22/23] Update README.md --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 84e69bb..c5aa1d5 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,34 @@ Sqids (pronounced "squids") is a small library that lets you generate YouTube-lo ## Getting started -@todo +Add sqids as a dependency to rebar.config + +```erlang +{deps, + [ + {sqids, {git, "https://github.com/sqids/sqids-erlang.git", {tag, "0.1.0"}}} + ] +}. +``` +Supported Erlang versions are + - Erlang/OTP 24 + - Erlang/OTP 25 + - Erlang/OTP 26 ## Examples -@todo +```erlang +1> Sqids = sqids:new(), +1> Id = sqids:encode([1, 2, 3], Sqids). +<<"86Rf07">> +2> sqids:decode(Id, Sqids). +[1,2,3] +3> MySqids = sqids:new(#{alphabet=><<"ABC123">>, min_length=>10, blocklist=>[]}), +3> MyId = sqids:encode([0], MySqids). +<<"A13C2B31AC">> +4> sqids:decode(MyId, MySqids). +[0] +``` ## License From a0d61306f7f2f194c1f8936319b897e614cec8a7 Mon Sep 17 00:00:00 2001 From: ts-klassen <104614062+ts-klassen@users.noreply.github.com> Date: Sun, 21 Jan 2024 16:33:40 +0900 Subject: [PATCH 23/23] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f318ac7..b7a5a74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ # CHANGELOG -@todo \ No newline at end of file +## UNRELEASED 0.1.0 +### New Feature +- Initial implementation