diff --git a/AUTHORS.adoc b/AUTHORS.adoc index de89cd30..1e79d459 100644 --- a/AUTHORS.adoc +++ b/AUTHORS.adoc @@ -16,3 +16,4 @@ - Johannes Kloos - Geoff Gole (@gsg) - Roma Sokolov (@little-arhat) +- David Sheets (@dsheets) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index a70fd23e..85ecba8e 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,5 +1,12 @@ = Changelog +== 0.22 + +- threads/CCLock: add `try_with_lock` to wrap `Mutex.try_lock` +- Add `CCMultiSet.remove_all` +- document errors in `CCIO` (close #86) +- use the new qtest/qcheck + == 0.21 - (breaking) make default `start`/`stop` arguments empty in printers (#82) diff --git a/README.adoc b/README.adoc index 5028ce5a..08156bc8 100644 --- a/README.adoc +++ b/README.adoc @@ -195,8 +195,6 @@ Iterators: === String -See http://cedeela.fr/~simon/software/containers/Containers_string[doc]. - In the module `Containers_string`: - `Levenshtein`: edition distance between two strings - `KMP`: Knuth-Morris-Pratt substring algorithm @@ -204,8 +202,6 @@ In the module `Containers_string`: === Advanced -See http://cedeela.fr/~simon/software/containers/Containers_advanced[doc]. - In the module `Containers_advanced`: - `CCLinq`, high-level query language over collections - `CCCat`, a few categorical structures diff --git a/_oasis b/_oasis index ba273d42..40bbc4d4 100644 --- a/_oasis +++ b/_oasis @@ -1,6 +1,6 @@ OASISFormat: 0.4 Name: containers -Version: 0.21 +Version: 0.22 Homepage: https://github.com/c-cube/ocaml-containers Authors: Simon Cruanes License: BSD-2-clause diff --git a/opam b/opam index d1bb2e04..cb986615 100644 --- a/opam +++ b/opam @@ -40,8 +40,6 @@ depopts: [ ] conflicts: [ "sequence" { < "0.5" } - "qtest" { < "2.2" } - "qcheck" ] tags: [ "stdlib" "containers" "iterators" "list" "heap" "queue" ] homepage: "https://github.com/c-cube/ocaml-containers/" diff --git a/setup.ml b/setup.ml index 657cf17a..e8ca7fc9 100644 --- a/setup.ml +++ b/setup.ml @@ -1,7 +1,7 @@ (* setup.ml generated for the first time by OASIS v0.4.4 *) (* OASIS_START *) -(* DO NOT EDIT (digest: 5a4456e803f365ce3410624a599bc76f) *) +(* DO NOT EDIT (digest: 0f90f90895c33b40b6535d64f1d40619) *) (* Regenerated by OASIS v0.4.7 Visit http://oasis.forge.ocamlcore.org for more information and @@ -7049,7 +7049,7 @@ let setup_t = { oasis_version = "0.4"; ocaml_version = Some (OASISVersion.VGreaterEqual "4.00.1"); - version = "0.21"; + version = "0.22"; license = OASISLicense.DEP5License (OASISLicense.DEP5Unit @@ -9689,7 +9689,7 @@ let setup_t = }; oasis_fn = Some "_oasis"; oasis_version = "0.4.7"; - oasis_digest = Some "v\210\236\240,\180C\195X\143\011\\\217Y\180\206"; + oasis_digest = Some "SzM\176?\172Sx\202\184\017a\207;\027f"; oasis_exec = None; oasis_setup_args = []; setup_update = false diff --git a/src/core/CCIO.mli b/src/core/CCIO.mli index eee9682d..fb91a491 100644 --- a/src/core/CCIO.mli +++ b/src/core/CCIO.mli @@ -46,6 +46,7 @@ val with_in : ?mode:int -> ?flags:open_flag list -> (** Open an input file with the given optional flag list, calls the function on the input channel. When the function raises or returns, the channel is closed. + @raise Sys_error in case of error (same as {!open_in} and {!close_in}) @param flags opening flags (default [[Open_text]]). [Open_rdonly] is used in any cases *) val read_chunks : ?size:int -> in_channel -> string gen @@ -77,12 +78,14 @@ val with_out : ?mode:int -> ?flags:open_flag list -> string -> (out_channel -> 'a) -> 'a (** Same as {!with_in} but for an output channel @param flags opening flags (default [[Open_creat; Open_trunc; Open_text]]). + @raise Sys_error in case of error (same as {!open_out} and {!close_out}) [Open_wronly] is used in any cases *) val with_out_a : ?mode:int -> ?flags:open_flag list -> string -> (out_channel -> 'a) -> 'a (** Similar to {!with_out} but with the [[Open_append; Open_creat; Open_wronly]] - flags activated, to append to the file *) + flags activated, to append to the file. + @raise Sys_error in case of error (same as {!open_out} and {!close_out}) *) val write_line : out_channel -> string -> unit (** Write the given string on the channel, followed by "\n" *) @@ -102,6 +105,7 @@ val with_in_out : ?mode:int -> ?flags:open_flag list -> string -> (in_channel -> out_channel -> 'a) -> 'a (** Combines {!with_in} and {!with_out}. @param flags opening flags (default [[Open_creat]]) + @raise Sys_error in case of error @since 0.12 *) (** {2 Misc for Generators} *) @@ -145,7 +149,7 @@ module File : sig (** [remove_exn path] tries to remove the file at [path] from the file system. - {b Raises} [Sys_error] if there is no file at [path]. + @raise Sys_error if there is no file at [path] or access rights are wrong. @since 0.8 *) val remove : t -> unit or_error @@ -159,11 +163,13 @@ module File : sig val read_dir : ?recurse:bool -> t -> t gen (** [read_dir d] returns a sequence of files and directory contained in the directory [d] (or an empty stream if [d] is not a directory) + @raise Sys_error in case of error (e.g. permission denied) @param recurse if true (default [false]), sub-directories are also explored *) val read_exn : t -> string (** Read the content of the given file, or raises some exception + @raise Sys_error in case of error @since 0.16 *) val read : t -> string or_error @@ -172,6 +178,7 @@ module File : sig val append_exn : t -> string -> unit (** Append the given string into the given file, possibly raising + @raise Sys_error in case of error @since 0.16 *) val append : t -> string -> unit or_error @@ -180,6 +187,7 @@ module File : sig val write_exn : t -> string -> unit (** Write the given string into the given file, possibly raising + @raise Sys_error in case of error @since 0.16 *) val write : t -> string -> unit or_error @@ -192,7 +200,8 @@ module File : sig (** Similar to {!read_dir} (with [recurse=true]), this function walks a directory recursively and yields either files or directories. Is a file anything that doesn't satisfy {!is_directory} (including - symlinks, etc.) *) + symlinks, etc.) + @raise Sys_error in case of error (e.g. permission denied) during iteration *) val show_walk_item : walk_item -> string @@ -204,5 +213,6 @@ module File : sig After [f] returns, the file is deleted. Best to be used in combination with {!with_out}. See {!Filename.temp_file} + @raise Sys_error in case of error @since 0.17 *) end diff --git a/src/core/META b/src/core/META index 5a4f08ce..d39e8bdb 100644 --- a/src/core/META +++ b/src/core/META @@ -1,6 +1,6 @@ # OASIS_START -# DO NOT EDIT (digest: 2366633cbce20e67d6074e01f20927dc) -version = "0.21" +# DO NOT EDIT (digest: a16455ed7ae2b2e8c42add2f54bd446e) +version = "0.22" description = "A modular standard library focused on data structures." requires = "bytes result" archive(byte) = "containers.cma" @@ -9,7 +9,7 @@ archive(native) = "containers.cmxa" archive(native, plugin) = "containers.cmxs" exists_if = "containers.cma" package "unix" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." requires = "bytes unix" archive(byte) = "containers_unix.cma" @@ -20,7 +20,7 @@ package "unix" ( ) package "top" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." requires = "compiler-libs.common containers containers.data containers.bigarray containers.string containers.unix containers.sexp containers.iter" @@ -32,7 +32,7 @@ package "top" ( ) package "thread" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." requires = "containers threads" archive(byte) = "containers_thread.cma" @@ -43,7 +43,7 @@ package "thread" ( ) package "string" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." requires = "bytes" archive(byte) = "containers_string.cma" @@ -54,7 +54,7 @@ package "string" ( ) package "sexp" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." requires = "bytes" archive(byte) = "containers_sexp.cma" @@ -65,7 +65,7 @@ package "sexp" ( ) package "iter" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." archive(byte) = "containers_iter.cma" archive(byte, plugin) = "containers_iter.cma" @@ -75,7 +75,7 @@ package "iter" ( ) package "io" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." requires = "bytes" archive(byte) = "containers_io.cma" @@ -86,7 +86,7 @@ package "io" ( ) package "data" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." requires = "bytes" archive(byte) = "containers_data.cma" @@ -97,7 +97,7 @@ package "data" ( ) package "bigarray" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." requires = "containers bigarray bytes" archive(byte) = "containers_bigarray.cma" @@ -108,7 +108,7 @@ package "bigarray" ( ) package "advanced" ( - version = "0.21" + version = "0.22" description = "A modular standard library focused on data structures." requires = "containers sequence" archive(byte) = "containers_advanced.cma" diff --git a/src/data/CCMultiSet.ml b/src/data/CCMultiSet.ml index 3b0bf680..c1f0c687 100644 --- a/src/data/CCMultiSet.ml +++ b/src/data/CCMultiSet.ml @@ -33,6 +33,10 @@ module type S = sig @raise Invalid_argument if [n < 0] @since 0.6 *) + val remove_all : t -> elt -> t + (** [remove_all set x] removes all occurrences of [x] from [set] + @since 0.22 *) + val update : t -> elt -> (int -> int) -> t (** [update set x f] calls [f n] where [n] is the current multiplicity of [x] in [set] ([0] to indicate its absence); the result of [f n] @@ -136,6 +140,8 @@ module Make(O : Set.OrderedType) = struct let remove ms x = remove_mult ms x 1 + let remove_all ms x = M.remove x ms + let update ms x f = let n = count ms x in match f n with diff --git a/src/data/CCMultiSet.mli b/src/data/CCMultiSet.mli index 8dcb6b9a..2f060dc2 100644 --- a/src/data/CCMultiSet.mli +++ b/src/data/CCMultiSet.mli @@ -33,6 +33,10 @@ module type S = sig @raise Invalid_argument if [n < 0] @since 0.6 *) + val remove_all : t -> elt -> t + (** [remove_all set x] removes all occurrences of [x] from [set] + @since 0.22 *) + val update : t -> elt -> (int -> int) -> t (** [update set x f] calls [f n] where [n] is the current multiplicity of [x] in [set] ([0] to indicate its absence); the result of [f n] diff --git a/src/threads/CCLock.ml b/src/threads/CCLock.ml index cd9aa456..c1f0bf14 100644 --- a/src/threads/CCLock.ml +++ b/src/threads/CCLock.ml @@ -35,6 +35,18 @@ let with_lock l f = assert_equal 10 (get l) *) +let try_with_lock l f = + if Mutex.try_lock l.mutex + then + try + let x = f l.content in + Mutex.unlock l.mutex; + Some x + with e -> + Mutex.unlock l.mutex; + raise e + else None + module LockRef = struct type 'a t = 'a lock let get t = t.content diff --git a/src/threads/CCLock.mli b/src/threads/CCLock.mli index 75e4b07c..354e47c0 100644 --- a/src/threads/CCLock.mli +++ b/src/threads/CCLock.mli @@ -18,6 +18,12 @@ val with_lock : 'a t -> ('a -> 'b) -> 'b the lock [l], in a critical section. If [f x] fails, [with_lock l f] fails too but the lock is released *) +val try_with_lock : 'a t -> ('a -> 'b) -> 'b option +(** [try_with_lock l f] runs [f x] in a critical section if [l] is not + locked. [x] is the value protected by the lock [l]. If [f x] + fails, [try_with_lock l f] fails too but the lock is released + @since 0.22 *) + (** Type allowing to manipulate the lock as a reference @since 0.13 *) module LockRef : sig @@ -48,7 +54,8 @@ val mutex : _ t -> Mutex.t (** Underlying mutex *) val get : 'a t -> 'a -(** Get the value in the lock. The value that is returned isn't protected! *) +(** Atomically get the value in the lock. The value that is returned + isn't protected! *) val set : 'a t -> 'a -> unit (** Atomically set the value