diff --git a/lib/cachet.ml b/lib/cachet.ml index fec54ff..f6d941b 100644 --- a/lib/cachet.ml +++ b/lib/cachet.ml @@ -478,6 +478,39 @@ let none : slice option = None let cache_miss t = t.metrics.cache_miss let cache_hit t = t.metrics.cache_hit +let map ({ fd; map; _ } as t) ~pos:logical_address logical_len = + let page = logical_address lsr t.pagesize in + let pos = page lsl t.pagesize in + (* round-down *) + let rem = logical_address - pos in + let len = rem + logical_len in + let len = + (* round-up *) + if ((1 lsl t.pagesize) - 1) land len != 0 then + (len + (1 lsl t.pagesize)) land lnot ((1 lsl t.pagesize) - 1) + else len + in + let off = logical_address land ((t.pagesize lsl 1) - 1) in + if len <= 1 lsl t.pagesize then begin + let hash = hash 0l (page lsl t.pagesize) land ((1 lsl t.cachesize) - 1) in + match t.arr.(hash) with + | Some { offset; length; payload } when offset == page lsl t.pagesize -> + t.metrics.cache_hit <- t.metrics.cache_hit + 1; + let len = Int.min (length - off) logical_len in + Bigarray.Array1.sub payload off len + | Some _ | None -> + t.metrics.cache_miss <- t.metrics.cache_miss + 1; + let { length; payload; _ } = load t logical_address in + let len = Int.min (length - off) logical_len in + Bigarray.Array1.sub payload off len + end + else begin + t.metrics.cache_miss <- t.metrics.cache_miss + 1; + let bstr = map fd ~pos len in + let len = Int.min (Bigarray.Array1.dim bstr - off) logical_len in + Bigarray.Array1.sub bstr off len + end + let load t ?(len = 1) logical_address = if len > 1 lsl t.pagesize then invalid_arg "Cachet.load: you can not load more than a page"; diff --git a/lib/cachet.mli b/lib/cachet.mli index ed81fe8..9a8400f 100644 --- a/lib/cachet.mli +++ b/lib/cachet.mli @@ -260,6 +260,18 @@ val fd : 'fd t -> 'fd val pagesize : 'fd t -> int (** [pagesize t] is the {i page-size} used by [t] (and specified on {!make}). *) +val map : 'fd t -> pos:int -> int -> Bstr.t +(** [map t ~pos len] returns a {!type:Bstr.t} which corresponds to a slice of + the {i block-device}. If this slice is smaller than or equal to a + {!val:pagesize}, the cache system is used to obtain the page and apply + {!val:Bstr.sub} to it (in other words, only a small allocation is made). + Otherwise, the {i syscall} {!type:map} is used. + + Regardless of the expected position [pos] or size [len], this function will + call the {i syscall} {!type:map} as the last analysis, with a position + aligned with the {!val:pagesize} and a size aligned with the + {!val:pagesize}. *) + val cache_hit : 'fd t -> int (** [cache_hit t] is the number of times a load hit the cache. *) diff --git a/test/test.ml b/test/test.ml index 081daab..6baba4c 100644 --- a/test/test.ml +++ b/test/test.ml @@ -36,6 +36,14 @@ let test01 = let b = Cachet.Bstr.to_string oracle in let a = List.of_seq a in let a = String.concat "" a in - Alcotest.(check string) "all" a b + Alcotest.(check string) "all" a b; + let a = Cachet.map t ~pos:2 (0x100 - 2) in + let a = Cachet.Bstr.to_string a in + let b = Cachet.Bstr.sub_string oracle ~off:2 ~len:(0x100 - 2) in + Alcotest.(check string) "map (round down)" a b; + let a = Cachet.map t ~pos:2 0x100 in + let a = Cachet.Bstr.to_string a in + let b = Cachet.Bstr.sub_string oracle ~off:2 ~len:0x100 in + Alcotest.(check string) "map (round up)" a b let () = Alcotest.run "cachet" [ ("simple", [ test01 ]) ]