emojid: use trie

This commit is contained in:
Swrup 2022-12-31 06:01:03 +01:00
parent 3fadbdecd3
commit 4032fbbed8
4 changed files with 71 additions and 50 deletions

View file

@ -295,7 +295,7 @@ let build_reply ~comment ~image_info ~tag_list ?parent_id user_id =
let date = Unix.time () in let date = Unix.time () in
let comment, citations = parse_comment comment in let comment, citations = parse_comment comment in
let* nick = User.get_nick user_id in let* nick = User.get_nick user_id in
let* emojid = Emojid.make_emojid id in let* emojid = Emojid.make id in
let reply = let reply =
{ id { id
; emojid ; emojid
@ -356,7 +356,7 @@ let make_post ~comment ?image_input ~tags ~op_or_reply_data user_id =
let post_exist id = Result.is_ok (Q.get_is_post id) let post_exist id = Result.is_ok (Q.get_is_post id)
let get_post id = let get_post id =
let* emojid = Emojid.Q.get_emojid id in let* emojid = Emojid.get id in
let* parent_id = Q.get_post_thread id in let* parent_id = Q.get_post_thread id in
let* user_id = Q.get_post_user_id id in let* user_id = Q.get_post_user_id id in
let* nick = User.get_nick user_id in let* nick = User.get_nick user_id in

View file

@ -31,6 +31,7 @@
conan conan
conan.string conan.string
conan-database.light conan-database.light
containers-data
directories directories
dream dream
emile emile

View file

@ -2,21 +2,48 @@ open Syntax
open Caqti_request.Infix open Caqti_request.Infix
open Caqti_type open Caqti_type
(* todo better: make emojid just string and not string list in this module;
problem is we have to split on unicode *)
module Q = struct module Q = struct
let upload_emojid = (* we save emojid in a string with emoji separated by '-' *)
let upload_emojid uuid emojid =
let emojid = String.concat "-" emojid in
Db.exec Db.exec
@@ (tup2 string string ->. unit) "INSERT INTO uuid_emojid VALUES (?,?)" ((tup2 string string ->. unit) "INSERT INTO uuid_emojid VALUES (?,?)")
(uuid, emojid)
let get_emojid = let get_emojid uuid =
Db.find @@ (string ->! string) "SELECT emojid FROM uuid_emojid WHERE uuid=?"
let is_free =
Db.find Db.find
@@ (string ->! int) ((string ->! string) "SELECT emojid FROM uuid_emojid WHERE uuid=?")
"SELECT EXISTS(SELECT 1 FROM uuid_emojid WHERE emojid=?)" uuid
|> Result.map (String.split_on_char '-')
let get_all_emojid () =
let* l =
Db.collect_list ((unit ->* string) "SELECT emojid FROM uuid_emojid") ()
in
Ok (List.map (String.split_on_char '-') l)
end end
let () = module Trie = CCTrie.Make (struct
type t = string list
type char_ = string
let compare = String.compare
let to_iter o f = List.iter f o
let of_list = Fun.id
end)
let max_emojid_lenght = 16
let alphabet =
Array.append Emoji.category_animals_and_nature Emoji.category_food_and_drink
let trie =
let tables = let tables =
[| (unit ->. unit) [| (unit ->. unit)
"CREATE TABLE IF NOT EXISTS uuid_emojid (uuid TEXT, emojid TEXT)" "CREATE TABLE IF NOT EXISTS uuid_emojid (uuid TEXT, emojid TEXT)"
@ -25,47 +52,35 @@ let () =
if if
Array.exists Result.is_error Array.exists Result.is_error
(Array.map (fun query -> Db.exec query ()) tables) (Array.map (fun query -> Db.exec query ()) tables)
then Dream.error (fun log -> log "can't create emojid's tables") then failwith "can't create emojid's tables"
else
match Q.get_all_emojid () with
| Error e ->
failwith (Format.sprintf "Error with Emojid.Q.select_all: %s" e)
| Ok l ->
let l = List.map (fun e -> (e, ())) l in
ref (Trie.of_list l)
let max_emojid_lenght = 16 let make uuid =
let emojis =
Array.append Emoji.category_animals_and_nature Emoji.category_food_and_drink
let is_free emojid =
let* is_free = Q.is_free emojid in
Ok (is_free = 0)
let upload_emojid uuid emojid =
let* is_free = is_free emojid in
if is_free then
let* () = Q.upload_emojid (uuid, emojid) in
Ok ()
else Error "Invalid emojid: already taken"
let make_emojid uuid =
(* pick a list of emojis *) (* pick a list of emojis *)
let random_emojis = let random_emojis =
Array.init max_emojid_lenght (fun _i -> List.init max_emojid_lenght (fun _i ->
let n = Random.int (Array.length emojis) in let n = Random.int (Array.length alphabet) in
Array.get emojis n ) Array.get alphabet n )
in in
(* pick the smallest emojid possible *) (* pick the smallest emojid possible *)
let* emojid, is_emojid = let longest_prefix = Trie.longest_prefix random_emojis !trie in
Array.fold_left (* add one more emoji to longest_prefix *)
(fun acc emoji -> match List.nth_opt random_emojis (List.length longest_prefix) with
match acc with | None ->
| Error e -> Error e Dream.error (fun log -> log "Emojid error: longest prefix is too long");
| Ok (s, is_emojid) -> Error "Could not create emojid"
if is_emojid then Ok (s, is_emojid) | Some x ->
else let emojid = longest_prefix @ [ x ] in
let s = s ^ emoji in let* () = Q.upload_emojid uuid emojid in
let* is_emojid = is_free s in trie := Trie.add emojid () !trie;
Ok (s, is_emojid) ) Ok (String.concat "" emojid)
(Ok ("", false))
random_emojis let get uuid =
in let* l = Q.get_emojid uuid in
if is_emojid then Ok (String.concat "" l)
let* () = upload_emojid uuid emojid in
Ok emojid
else Error "couldn't find a free emojid"

5
src/emojid.mli Normal file
View file

@ -0,0 +1,5 @@
(** [make uuid] creates an emojid for [uuid]; hopefully returns [Ok emojid] *)
val make : string -> (string, string) result
(** [get uuid] is [Ok emoji] if [uuid] has an emojid *)
val get : string -> (string, string) result