diff --git a/AUTHORS.adoc b/AUTHORS.adoc index f2be0ad2..007ed5ce 100644 --- a/AUTHORS.adoc +++ b/AUTHORS.adoc @@ -17,3 +17,4 @@ - Geoff Gole (@gsg) - Roma Sokolov (@little-arhat) - Malcolm Matalka (`orbitz`) +- David Sheets (@dsheets) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 506ae9df..85ecba8e 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,5 +1,27 @@ = 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) + +- add `CCFormat.{with_color_sf,fprintf_dyn_color,sprintf_dyn_color}` +- add `CCFormat.Dump` for easy debugging (see #82) +- add `CCArray.Sub.to_list` +- add `CCArray.{sorted,sort_indices,sort_ranking}` (closes #81) + +- handle '\r` in CCSexpM (fixes #83) +- add alias `Containers.IO` +- bugfixes in `CCArray.Sub` +- bugfix + tests for `CCArray.Sub.sub` +- disable parallel build to support cygwin + == 0.20 - bugfix in `CCArray.equal` diff --git a/README.adoc b/README.adoc index 0bb2bf97..62877090 100644 --- a/README.adoc +++ b/README.adoc @@ -180,7 +180,6 @@ Iterators: - `CCKList`, a persistent iterator structure (akin to a lazy list, without memoization) - `CCKTree`, an abstract lazy tree structure - === Thread In the library `containers.thread`, for preemptive system threads: diff --git a/_oasis b/_oasis index 646621b2..60512027 100644 --- a/_oasis +++ b/_oasis @@ -1,6 +1,6 @@ OASISFormat: 0.4 Name: containers -Version: 0.20 +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 a2874818..066583a7 100644 --- a/opam +++ b/opam @@ -38,8 +38,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/src/core/CCFormat.mli b/src/core/CCFormat.mli index 6a826b15..187577dd 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -173,7 +173,7 @@ val with_color_sf : string -> ('a, t, unit, string) format4 -> 'a CCFormat.with_color_sf "red" "%a" CCFormat.Dump.(list int) [1;2;3] |> print_endline;; ]} {b status: experimental} - @since NEXT_RELEASE *) + @since 0.21 *) (** {2 IO} *) @@ -207,7 +207,7 @@ val sprintf_dyn_color : colors:bool -> ('a, t, unit, string) format4 -> 'a CCFormat.sprintf_dyn_color ~colors:false "@{%a@}" CCFormat.Dump.(list int) [1;2;3] |> print_endline;; ]} - @since NEXT_RELEASE *) + @since 0.21 *) val fprintf : t -> ('a, t, unit ) format -> 'a (** Alias to {!Format.fprintf} @@ -215,7 +215,7 @@ val fprintf : t -> ('a, t, unit ) format -> 'a val fprintf_dyn_color : colors:bool -> t -> ('a, t, unit ) format -> 'a (** Similar to {!fprintf} but enable/disable colors depending on [colors] - @since NEXT_RELEASE *) + @since 0.21 *) val ksprintf : f:(string -> 'b) -> @@ -246,7 +246,7 @@ val to_file : string -> ('a, t, unit, unit) format4 -> 'a [| [1, true; 2, false]; []; [42, false] |];; ]} - @since NEXT_RELEASE *) + @since 0.21 *) module Dump : sig type 'a t = 'a printer diff --git a/src/core/CCIO.mli b/src/core/CCIO.mli index 6ce18d4b..6319fb25 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} *) @@ -144,7 +148,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 @@ -158,11 +162,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 @@ -171,6 +177,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 @@ -179,6 +186,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 @@ -191,7 +199,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 diff --git a/src/core/containers.ml b/src/core/containers.ml index bb89ba14..79bdf24a 100644 --- a/src/core/containers.ml +++ b/src/core/containers.ml @@ -83,4 +83,3 @@ module String = struct include CCString end module Vector = CCVector - diff --git a/src/data/CCMultiSet.ml b/src/data/CCMultiSet.ml index 8f33b3f8..39d13019 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