anti scroll

ブラウザと小説の新しい関係を模索する

ocaml memcached client 半端バージョン

 Ocaml で memcached client を作ってみようと思って作りかけていたのですが、似たような仕組みでOcaml純正の素晴らしげなものがリリースされてしまったので、多分終了。悔しいので、コードだけ晒しておきます。

 あんまり良くわかっていないのですが、get/set だけできたらいいよねーというノリで作ったものです。

 C#のクライアントとかを眺めると、結構面倒くさそうなことをしてるのですが、きちんとしようとすると色々あるんでしょうね。

open Unix

type operation_result =
    | Timeout of float
    | Failed of string
    | Connected
    | SetOK
    | GetOK of string

let (@@) f x = f x

let flush () =
  flush Pervasives.stdout

let spf = Printf.sprintf

let primary_buffer = String.create 8192

let input_buffer = Buffer.create 8192

let add_crlf data =
  spf "%s\r\n" data

let set_cmd key flgs exp len =
  add_crlf @@ spf "set %s %d %d %d" key flgs exp len

let get_cmd key =
  add_crlf @@ spf "get %s" key

let read_line sock =
  let s1 = String.create 1 in
  let buff = Buffer.create 1024 in
  let rec iter () =
    let nr = Unix.read sock s1 0 1 in
      match nr, s1 with
	| 1, "\r" ->
	    iter ()
	| 1, "\n" ->
	    Buffer.contents buff
	| 1, other ->
	    Buffer.add_string buff s1;
	    iter ()
	| _, _ ->
	    Buffer.contents buff in
    iter ()

let read_bytes sock bytes =
  let () = Buffer.clear input_buffer in
  let rec iter rest =
    if rest > 0 then
      let nr = Unix.read sock primary_buffer 0 rest in
      let () = Buffer.add_substring input_buffer primary_buffer 0 nr in
	iter (rest - nr)
    else
      Buffer.contents input_buffer in
    iter bytes

let start_connect sock addr =
  let ch = Event.new_channel () in
  let _ =
    Thread.create
      (fun () ->
	Unix.connect sock addr;
	Event.sync @@ Event.send ch Connected)
      () in

    Event.receive ch

let start_set sock key data =
  let ch = Event.new_channel () in
  let _ =
    Thread.create
      (fun () ->
	let cmd = set_cmd key 0 0 (String.length data) in
	let _ = Unix.write sock cmd  0 (String.length cmd) in
	let data_crlf = add_crlf data in
	let _ = Unix.write sock data_crlf 0 (String.length data_crlf) in
	  match read_line sock with
	    | "STORED" -> Event.sync (Event.send ch SetOK)
	    | line -> Event.sync (Event.send ch @@ (Failed (spf "set(invalid rsp %s)" line))))
      () in

    Event.receive ch

let start_get sock key =
  let ch = Event.new_channel () in
  let _ =
    Thread.create
      (fun () ->
	let cmd = get_cmd key in
	let _ = Unix.write sock cmd 0 (String.length cmd) in
	let line = read_line sock in
	  match Str.split (Str.regexp " ") line with
	    | ["VALUE"; k; flags; bytes] ->
		let data = read_bytes sock @@ int_of_string bytes in
		let _ = read_line sock in (* \r\n *)
		let _ = read_line sock in (* END *)
		  Event.sync (Event.send ch (GetOK data))
	    | _ ->
		Event.sync (Event.send ch (Failed(spf "get(invalid rsp %s)" line))))
      () in
    
    Event.receive ch

let start_wdt second =
  let ch = Event.new_channel () in
  let _ =
    Thread.create
      (fun () ->
	Thread.delay second;
	Event.sync (Event.send ch (Timeout second)))
      () in

    Event.receive ch

let wait_event time channel =
  Event.sync @@ Event.choose [channel; start_wdt time]

let connect ?(timeout=3.00) sock addr =
  wait_event timeout (start_connect sock addr)

let set ?(timeout=3.00) sock ~key ~value =
  wait_event timeout (start_set sock key value)

let get ?(timeout=3.00) sock key =
  wait_event timeout (start_get sock key)

socket はリンクするアプリケーションで管理してもらうことになるのですが、C#のクライアントをみると、ライブラリがソケット管理も吸収する作りになっているみたい。