Compare commits

...

1177 commits
1.5 ... main

Author SHA1 Message Date
Simon Cruanes
5461dcc07a
prepare for 3.17
Some checks failed
format / format (push) Has been cancelled
Build and Test / build (push) Has been cancelled
2026-01-12 20:16:42 -05:00
Simon Cruanes
d4fdff884f
more benchs
Some checks are pending
format / format (push) Waiting to run
Build and Test / build (push) Waiting to run
2026-01-11 13:52:16 -05:00
Simon Cruanes
eab2e1d33f
try to make the test_random uniformity test more robust
Some checks failed
format / format (push) Has been cancelled
Build and Test / build (push) Has been cancelled
just increase the sample size.
2025-12-20 11:19:45 -05:00
Simon Cruanes
c72b60fd6f
do simplify the code a bit
closer to the original solution, too. Avoids a redundant `:= []`.
2025-12-20 11:15:15 -05:00
Simon Cruanes
ddc87518a7
another test for lists 2025-12-20 11:08:39 -05:00
Simon Cruanes
15b421c54e
faster List.take_drop thanks to a trick by nojb
https://discuss.ocaml.org/t/efficient-implementation-of-list-split/17616/2
pretty cool.
2025-12-20 11:08:30 -05:00
Simon Cruanes
c1b13f1c7f
feat: add CCAtomic.update_cas
Some checks failed
format / format (push) Has been cancelled
Build and Test / build (push) Has been cancelled
2025-12-08 13:41:29 -05:00
Simon Cruanes
f51b56ffbc
cleanup
Some checks failed
format / format (push) Has been cancelled
Build and Test / build (push) Has been cancelled
2025-11-25 20:38:19 -05:00
Simon Cruanes
02c4d51fd0
chore: CI 2025-11-25 20:12:06 -05:00
Simon Cruanes
7c8adbd9fc
move to ocamlformat 0.27, format code 2025-11-25 20:11:54 -05:00
Simon Cruanes
954ea61d22
doc + benchs 2025-11-25 20:04:47 -05:00
Simon Cruanes
b069461fe2
test: enrich pvec test 2025-11-25 20:01:16 -05:00
Simon Cruanes
f13fb6f471
feat pvec: add flat_map 2025-11-25 19:59:23 -05:00
Simon Cruanes
01402388e4
fix warning 2025-11-25 19:21:11 -05:00
István Donkó
14ad490c7e fix: insert missing symbol into range doc comments
Some checks failed
Build and Test / build (push) Has been cancelled
Build and Test / format (push) Has been cancelled
2025-10-27 12:03:46 -04:00
Simon Cruanes
3b49ad2a4e
Merge pull request #478 from jmid/cbor-roundtrip-prop-patch
Some checks failed
Build and Test / build (push) Has been cancelled
Build and Test / format (push) Has been cancelled
Patch CBor round-trip QCheck test to hold for nan's too
2025-07-11 15:22:25 -04:00
Jan Midtgaard
1a11459991 Auto-format code 2025-07-09 16:06:47 +02:00
Jan Midtgaard
0290aa9754 Use CCList.equal for backward compatibility 2025-07-09 15:48:24 +02:00
Jan Midtgaard
9df429005d Patch CBor roundtrip property to hold for nan's too 2025-07-09 12:25:39 +02:00
Simon Cruanes
99dba20fa6
prepare for 3.16
Some checks failed
Build and Test / build (push) Has been cancelled
Build and Test / format (push) Has been cancelled
2025-05-27 09:22:31 -04:00
Simon Cruanes
f934db1e9c
fix: compat with OCaml 5.4
Some checks are pending
Build and Test / build (push) Waiting to run
Build and Test / format (push) Waiting to run
close #477
2025-05-26 23:44:02 -04:00
Simon Cruanes
14ad8c1f2a
format
Some checks failed
Build and Test / build (push) Has been cancelled
Build and Test / format (push) Has been cancelled
2025-05-06 22:24:04 -04:00
Simon Cruanes
0ff9614520
feat: add containers.leb128 library
adapted from pbrt
2025-05-06 21:53:26 -04:00
Simon Cruanes
ab7d0fcc09
fix: oob(!!) in CCHash.bytes
Some checks are pending
Build and Test / build (push) Waiting to run
Build and Test / format (push) Waiting to run
2025-05-06 10:01:31 -04:00
Simon Cruanes
b55d3cfe6a
tests for hashing strings 2025-05-06 10:01:31 -04:00
Simon Cruanes
4613aafb30
feat: add CCFun.with_return
Some checks failed
Build and Test / build (push) Has been cancelled
Build and Test / format (push) Has been cancelled
2025-02-07 16:57:56 -05:00
Adlertz, Niclas
4294dc7ca3 Add square brackets in comment for CCList.return
Some checks failed
Build and Test / build (push) Has been cancelled
Build and Test / format (push) Has been cancelled
2025-01-28 22:19:11 -05:00
Simon Cruanes
31ad563044
Merge pull request #467 from mobotsar/enrich_char
Additional functions for the `Char` module to check common character properties
2025-01-04 17:09:24 -05:00
Alexander
2dcaa12fb7 Fixed docstring typo. 2025-01-04 11:19:57 -05:00
Alexander
bace9fe209 Fixed tests to work with older OCaml versions that lack String.for_all. 2025-01-04 10:11:03 -05:00
Alexander
1486cbf5a1 Added tests for CCChar predicates. 2025-01-04 10:00:36 -05:00
Alexander
b95e2de65b Added functions to the Char module to check common character properties. 2025-01-04 09:18:51 -05:00
Simon Cruanes
f310bc5771
more CI 2025-01-03 21:22:05 -05:00
Simon Cruanes
6d962a70d0
grr CI 2025-01-03 13:10:23 -05:00
Simon Cruanes
517d4605d5
try to update CI 2025-01-03 12:58:59 -05:00
Simon Cruanes
b0f673fbbb
add more tests for CCString.{r,}take_while 2025-01-03 12:54:01 -05:00
Simon Cruanes
c6f6a012b4
Merge pull request #465 from mobotsar/main
Add `take_while` and `rtake_while` to `CCString`, addressing #463
2025-01-03 12:48:57 -05:00
Alexander Lucas
1e06423e87 Fixed formatting of t_string.ml tests for take_while, etc. 2025-01-01 10:33:05 -05:00
Alexander Lucas
8bb3801a52 Fixed formatting of CCString.rtake_while. 2025-01-01 10:22:42 -05:00
Alexander Lucas
d29ed7ee72 Renamed predicate parameter of take_while, rtake_while from p to f, aligining it with pre-existing drop_while. 2025-01-01 09:55:26 -05:00
Alexander Lucas
330cba94de added tests for take_while, rtake_while. 2025-01-01 09:46:22 -05:00
Alexander Lucas
699b370220 Updated String and StringLabels interfaces to reflect take_while, rtake_while. 2025-01-01 09:34:06 -05:00
mobotsar
85ca948012
Merge branch 'c-cube:main' into main 2025-01-01 09:19:25 -05:00
mobotsar
6c8569a7d9
Update CCString.mli to align parameter names in mli descriptions and implementations. 2025-01-01 00:48:58 -05:00
mobotsar
1498158a4f
Update CCString.mli for take_while, rtake_while 2025-01-01 00:47:17 -05:00
mobotsar
d8c00f96be
Update CCString.ml with take_while, rtake_while
Added two functions to the `CCString` module addressing #463 following the style of `CCString.drop_while` and `CCString.rdrop_while`. Corresponding `CCString.mli` changes to follow.
2025-01-01 00:35:55 -05:00
mobotsar
510db54150 Update CCEither.mli fixing type in docstring
Changed "form OCaml 4.12" to "from OCaml 4.12".
2024-12-31 23:05:02 -05:00
mobotsar
2e8d70f073
Update CCEither.mli fixing type in docstring
Changed "form OCaml 4.12" to "from OCaml 4.12".
2024-12-31 22:25:55 -05:00
Simon Cruanes
2fda76a5f7
factor implem for Vec.{find,find_i} 2024-12-13 00:19:05 -05:00
Simon Cruanes
cad41d70d6
ocamlformat 2024-12-13 00:17:53 -05:00
John Hester
b140a50c46 feat: add CCVector.findi 2024-12-13 00:15:27 -05:00
Simon Cruanes
e1de3da1e3
prepare for 3.15 2024-11-18 12:22:16 -05:00
Simon Cruanes
f02df05b35
additional fix for #454 on OCaml 4.10..4.12 2024-11-18 12:22:16 -05:00
Simon Cruanes
477e9cc7ca
remove warning in tests 2024-11-18 12:22:15 -05:00
Simon Cruanes
921db438f2
chore: in CI, run tests in release profile
this is required to be able to repro #454
2024-11-18 12:22:15 -05:00
Simon Cruanes
303f51f93d
chore: add 4.10 to compat matrix 2024-11-18 12:22:15 -05:00
Simon Cruanes
6dcaddb7c6
fixes and format 2024-11-15 11:02:38 -05:00
seprov
8d8f1d4145
Add split_result (#459)
add `CCList.split_result` (@seprov)
2024-11-15 11:01:20 -05:00
Fardale
9cfedad7ba feat: add pretty printer to CCMultiSet 2024-11-05 18:25:51 +01:00
Simon Cruanes
9bef25b6e2
format 2024-10-02 12:31:53 -04:00
Simon Cruanes
afb93cfc43
Merge pull request #457 from gmevel/linear-heap-building
Improvements to CCHeap
2024-10-02 10:04:42 -04:00
Simon Cruanes
3c530f47f4
Merge branch 'main' into linear-heap-building 2024-10-02 10:04:34 -04:00
Simon Cruanes
3efaa02d9d
update test output 2024-09-17 14:50:42 -04:00
Simon Cruanes
69f0e9b624
test: better regression test for #454 2024-09-17 14:04:09 -04:00
Simon Cruanes
c959e396b3
fix #454: work around a weird miscompilation 2024-09-17 12:51:07 -04:00
Simon Cruanes
6ab811f79b
prepare for 3.14 2024-09-10 08:47:46 -04:00
Simon Cruanes
9f8c2efe64
add missing @since tags 2024-09-03 13:12:12 -04:00
Simon Cruanes
07cfdb0d94
format 2024-09-03 13:11:57 -04:00
Simon Cruanes
5abb63517c
Merge pull request #458 from benbellick/add-predicate-combinator
predicate combinators: and_p and or_p
2024-09-03 13:11:50 -04:00
Benjamin Bellick
c6cb572230 and_p -> and_pred, or_p -> or_pred 2024-09-03 11:59:43 -05:00
Ben Bellick
1dc046c6e9 fix formatting 2024-08-24 10:53:59 -05:00
Ben Bellick
df0e442956 predicate combinators: and_p and or_p 2024-08-24 10:26:01 -05:00
Simon Cruanes
65fc920427
feat pp: add a bunch of extensions
thanks to @grayswandyr
2024-08-19 12:17:58 -04:00
Glen Mével
5d315503e1 CCHeap: building from almost-sorted sequences 2024-08-09 14:46:17 +02:00
Glen Mével
a24e1f7472 doc/CCHeap: fix English, improve wording, add sections 2024-07-31 17:50:35 +02:00
Glen Mével
13db1c31e9 tests/CCHeap: add tests 2024-07-31 16:03:06 +02:00
Glen Mével
92676f5513 tests/CCHeap: reorder tests 2024-07-31 03:27:52 +02:00
Glen Mével
78e67a9f4a tests/CCHeap: improve existing tests
- label all tests
- decouple tests about different heap functions
- random instances now have better coverage of possible cases:
  + more variability in size
    (previously, some tests were limited to a fixed size)
  + high probability of duplicates
    (previously, the probability of duplicates was negligible,
    because elements were drawn uniformly from the full `int` range)
- the test for `of_list, take_exn` is now more precise
  (added a duplicate element)
- the test for `to_list_sorted` is now more precise
  (checks that the resulting list is what we want,
  instead of just checking that it is sorted)
- the test for `filter` is now more precise
  (also checks that no element has been spuriously dropped)
- more uniform style for easier reading, using `|>`
2024-07-31 03:27:52 +02:00
Glen Mével
fdfc806afb CCHeap: avoid boxing in delete_one 2024-07-31 03:27:52 +02:00
Glen Mével
6c810eb83d doc/CCHeap: document (==) for merge 2024-07-31 03:27:52 +02:00
Glen Mével
3f95fd44e6 perf/CCHeap: filter, delete_all in O(n) and ensure (==)
- for `delete_all` this is a bugfix
  (physical equality was documented but not implemented)
- `delete_one` is unchanged, it already had complexity O(n)
  and ensured physical equality
2024-07-31 03:27:49 +02:00
Glen Mével
806bb8c7bc perf/CCHeap: heap building in O(n) 2024-07-31 02:19:33 +02:00
Glen Mével
cc2dd6d829 doc/CCHeap: move filter down 2024-07-27 04:57:43 +02:00
Glen Mével
8349a4d244 doc/CCHeap: fix grammar, improve doc of delete_{one,all} 2024-07-27 04:57:41 +02:00
Glen Mével
793bad1e5b doc/CCHeap: document complexities
Committing to these complexities in documentation is not a constraint
for representation of heaps, because they are achieved by every
well-known representation (for some of them, in amortized time):

https://en.wikipedia.org/wiki/Template:Heap_Running_Times

- `find_min`: O(1)
- `take`: O(log n)
- `insert`: O(log n)
- `merge`: O(log(m+n)) (excepted binary heaps which only achieve O(m+n))
- `add_seq`: O(n log(m+n)) (trivially, by repeated insertion)
  + this can be improved to O(log(m) + n), regardless of the
    representation of heaps (to be done in a later commit)
- `of_seq`: O(n log n) (ditto: can be improved to O(n))

Less trivial:

- `filter`, `delete_{one,all}`:

  + O(n) can be achieved for any reasonable representation of heaps, by
    using `of_seq` and `to_seq` which, as said, can always be made O(n).

  + With the current implementation, it is not obvious, but the
    complexity of `filter` and `delete_all` is Θ(n log n); the
    complexity of `delete_one` is O(n). Indeed, node rebuilding with
    `_make_node` is in O(1), merging is in Θ(log n), and every element
    deletion induces one merge; there are heap instances that achieve
    the worst case Ω(n log n), for instance:

                     x
                    / \
                   x   y
                  / \
                ...  y
                /
               x
              / \
             h   y

    with n/3 occurrences of x, n/3 occurrences of y, a sub-heap h of n/3
    elements, and when y is greater than all elements of h; then,
    deleting all occurrences of x performs the following computation:

        merge (merge (merge (merge h y) …) y) y

    where each `merge` takes time Θ(log n).
2024-07-27 04:55:40 +02:00
Glen Mével
8666faf257 doc/CCHeap: uniformize doc of conversion functions 2024-07-26 22:34:13 +02:00
Glen Mével
6bd5d3aacf doc/CCHeap: reorder conversion functions 2024-07-26 22:17:05 +02:00
Simon Cruanes
02ac5bd78a
add @since tags 2024-07-19 14:06:06 -04:00
Simon Cruanes
cb14c0d04b
format 2024-07-19 14:04:00 -04:00
NoahBatchelor
e933995733
Kleisli Composition Operator and Apply_or Added (#455)
Added the Kleisli composition operator for Option, Result, and CCFun.
2024-07-19 14:03:52 -04:00
Simon Cruanes
60bd3ae1d6 perf: use a monomorphic impl for CCMonomorphic.{min,max}
close #452
2024-07-09 10:13:50 -04:00
Simon Cruanes
99bfa200af
CI: compile on 5.2 2024-05-16 14:21:57 -04:00
Simon Cruanes
944410d3c7
CI: run all tests on linux and various OCaml versions
and run macOS stuff only for 5.1
2024-05-13 22:36:16 -04:00
Simon Cruanes
2e276002c6
format 2024-05-13 22:06:31 -04:00
Simon Cruanes
53b3f75d64
docs 2024-05-13 21:54:33 -04:00
Simon Cruanes
f5aa4de6e7
add CCByte_buffer.to_slice 2024-05-13 21:52:46 -04:00
Simon Cruanes
c29083c216
richer API for byte_buf 2024-05-13 21:51:27 -04:00
Simon Cruanes
14dc772eba
format 2024-05-13 21:43:29 -04:00
Simon Cruanes
040fe2f97c
move to dune 3.0, fix warnings 2024-05-13 21:42:54 -04:00
Simon Cruanes
8eaa2b6429
improve API for byte slice 2024-05-13 21:34:05 -04:00
Simon Cruanes
5a56269b6f
gitignore 2024-05-13 21:07:08 -04:00
Simon Cruanes
c299b32309
helper script to run benchs 2024-05-13 21:05:55 -04:00
Simon Cruanes
8b60f52377
add byte_slice module, fix warnings 2024-05-13 21:05:06 -04:00
Simon Cruanes
4ff604015c
rework CI a tiny bit 2024-05-13 21:00:30 -04:00
Simon Cruanes
0b0dd83423
reformat all the things 2024-05-13 20:57:53 -04:00
Simon Cruanes
042d5b4f68
refactor byte buf: make the type public
it's time to let the types roam free, people.
2024-05-13 20:56:49 -04:00
Simon Cruanes
94b67deda4
remove dead file 2024-05-13 20:56:44 -04:00
Simon Cruanes
fcd4d3f6ec
add cons_when to CCListLabels 2024-04-19 09:52:13 -04:00
Nicola Mometto
4ff1853222
feat(CCList): add cons_when 2024-04-19 09:52:13 -04:00
Simon Cruanes
570e3f8d67
Merge pull request #450 from Bronsa/nicola/tup_pipe
feat(CCFun): add (||>)
2024-04-11 10:25:47 -04:00
Nicola Mometto
71233f2c1a chore: add since NEXT_RELEASE 2024-04-11 14:58:56 +01:00
Nicola Mometto
6a70c57253 feat(CCFun): add (|||>) 2024-04-11 14:54:07 +01:00
Nicola Mometto
2a21181580 feat(CCFun): add (||>) 2024-04-11 14:51:55 +01:00
Simon Cruanes
69cd3ca78d
Merge pull request #448 from c-cube/wip-pvec
containers.pvec
2024-01-16 14:25:58 -05:00
Simon Cruanes
41d8a7a968
add Pvec.equal 2024-01-16 14:20:09 -05:00
Master Builder
17eab9c3f4 CCVector: Add function foldi 2024-01-11 12:19:12 -05:00
Simon Cruanes
cb949e4c7f
more benchs 2024-01-10 12:48:33 -05:00
Simon Cruanes
813ea40ac5
comment 2024-01-08 23:49:55 -05:00
Simon Cruanes
b49f358d47
perf: more bench for pvec 2024-01-08 12:53:26 -05:00
Simon Cruanes
821fa6e3cf
more tests 2024-01-08 09:19:39 -05:00
Simon Cruanes
dd552fe334
more tests 2024-01-07 23:30:12 -05:00
Simon Cruanes
b9cc91fb96
pvec: implement iter_rev directly 2024-01-07 23:21:32 -05:00
Simon Cruanes
12ff3802ce
perf: implement iter separately from iteri 2024-01-07 23:17:57 -05:00
Simon Cruanes
a281476082
perf: reduce GC pressure by using a branching factor of 16 2024-01-07 23:17:39 -05:00
Simon Cruanes
8dca0ea78d
fix build 2024-01-06 22:45:42 -05:00
Simon Cruanes
04440deb39
small refactor 2024-01-06 17:17:06 -05:00
Simon Cruanes
81408b8e1b
add last to Pvec 2024-01-05 22:54:08 -05:00
Simon Cruanes
6a3cafa763
compat 2024-01-05 22:38:05 -05:00
Simon Cruanes
b9b6bf82b6
perf: restore branching factor to 32 2024-01-05 22:14:22 -05:00
Simon Cruanes
42967b2127
benchs: add pvec 2024-01-05 22:14:18 -05:00
Simon Cruanes
03e253a31c
fix pvec tests
(make choose's result irrelevant, only test whether it returns)
2024-01-05 21:54:41 -05:00
Simon Cruanes
66b42ea944
fixes for pvec 2024-01-05 21:54:38 -05:00
Simon Cruanes
209ee3a3ed
fix warning in test 2024-01-05 21:47:40 -05:00
Simon Cruanes
dd0e23cea2
add tests for pvec 2024-01-05 21:47:25 -05:00
Simon Cruanes
7b7eda5a05
wip: persistent vectors based on clojure's 2024-01-05 21:47:09 -05:00
Gabriel Scherer
9de8f1fb2e CCVector: fix two labels-omitted warnings
These warnings are silenced by the use of -nolabels in the compilation
flags (which I understand is designed to make the life of the
CC*Labels file easier, not let minor labeling mistakes sleep in.)
2024-01-04 16:44:34 -05:00
Simon Cruanes
60bb2c8c68
prepare for 3.13.1 2023-12-20 14:41:48 -05:00
Simon Cruanes
884d354986
fix test 2023-12-20 14:41:48 -05:00
Simon Cruanes
8dc4d5a706
fix: overshoot, concat_map is only TRMC after 5.1 2023-12-20 14:38:39 -05:00
Simon Cruanes
9ebfbe1c2d
boooooost CI 2023-12-15 22:37:58 -05:00
Simon Cruanes
d6fe4db6a2
fixity fix 2023-12-15 22:37:57 -05:00
Simon Cruanes
81f410649e
list: TRMC was in 4.14, we can use it earlier 2023-12-15 22:37:57 -05:00
Simon Cruanes
ad2ceb6e13
perf: use concat_map for CCList.flat_map on >= 5.1 2023-12-15 22:37:35 -05:00
Simon Cruanes
c4dcf1efe2
fix insidious bug in CCList.flat_map
we have been accidentally relying on evaluation order.
2023-12-15 22:36:39 -05:00
Simon Cruanes
7436727942
test: sanity test for flat_map 2023-12-15 22:36:21 -05:00
Simon Cruanes
8b53966dff
changelog 2023-12-06 09:21:39 -05:00
Simon Cruanes
7c1ca1d82f
prepare for 3.13 2023-12-05 16:09:29 -05:00
Simon Cruanes
f68d187142
fix stupid bug 2023-12-05 15:02:41 -05:00
Simon Cruanes
4682f9787b
tweak tweak tweak 2023-12-05 14:35:25 -05:00
Simon Cruanes
33053a1f96
finish removing threads 2023-12-05 13:45:48 -05:00
Simon Cruanes
bf2375f042
delete containers-thread
use moonpool instead!
2023-12-05 13:04:51 -05:00
Simon Cruanes
98ceaac8de
detail 2023-12-05 13:03:53 -05:00
Simon Cruanes
36790cf3ed
bugfix 2023-12-05 12:19:15 -05:00
Simon Cruanes
7fcf26963b
ensure unfold is tailrec 2023-12-05 12:04:55 -05:00
Ben Bellick
73e68dae7c CCList: add unfold 2023-12-05 11:59:55 -05:00
Simon Cruanes
50b478366f fix for 4.08 2023-12-03 20:03:16 -05:00
Simon Cruanes
fcee2f9c41 perf: accelerate List.append and List.flat_map on 5.1 2023-12-03 20:03:16 -05:00
Simon Cruanes
ec9148cf81 update benchs to add 2 distinct flat_map to it 2023-12-03 20:03:16 -05:00
Simon Cruanes
4d581498ae chore: add scripts to run benchmarks 2023-12-03 20:03:16 -05:00
Master Builder
77bfa34355 CCBool: Add functions if_then and if_then_else 2023-11-26 23:31:36 -05:00
Simon Cruanes
fdb7c0f4b0
remove opam dependencies, broken currently 2023-11-26 23:31:08 -05:00
Simon Cruanes
19e3dc9b44
fix CI 2023-11-24 11:06:43 -05:00
Simon Cruanes
49d66def49
Merge pull request #441 from c-cube/wip-rely-on-trmc
rely on TRMC more
2023-11-20 19:05:12 -05:00
Simon Cruanes
037c55a43d
tailrec 2023-11-19 23:51:47 -05:00
Simon Cruanes
517fd99a5f add test for nested ansi color 2023-11-19 22:27:51 -05:00
Simon Cruanes
ba9ef1f453 breaking: pp: modify Ext.t so it takes surrounding value
The type is now opaque, using a smart constructor, and is passed
the value used in the closest surrounding call to this extension,
if any. It is used by `Term_color` to properly restore ANSI
style in nested situations.
2023-11-19 22:27:51 -05:00
Simon Cruanes
1b3ddb7adf
fix test 2023-11-19 22:25:17 -05:00
Simon Cruanes
8c224e42fd
CCList: remove some functions that are subsumed by the stdlib 2023-11-19 21:56:10 -05:00
Simon Cruanes
3bd95d257c
CCList: use TRMC for many functions on 5.1 2023-11-19 21:47:57 -05:00
Simon Cruanes
1b026f267c
tests: update t_list to check more functions for tail-rec safety 2023-11-19 21:47:39 -05:00
Simon Cruanes
b16385bb9d
CI: test on 5.1 2023-11-19 21:47:28 -05:00
Simon Cruanes
51cb8e2992
feat: cpp: handle iflt and ifgt 2023-11-19 21:47:14 -05:00
Simon Cruanes
71dcc0937c
benchs: add funvec.flatten, quite terrible 2023-11-16 15:32:13 -05:00
Simon Cruanes
3a296ba127
feat ccfunvec: add fold_rev 2023-11-16 15:32:07 -05:00
Simon Cruanes
a07c688404
fix benchs somewhat 2023-11-16 14:42:29 -05:00
Simon Cruanes
1508b6c940 add Containers_pp.newline_or_spaces 2023-11-13 14:32:59 -05:00
Simon Cruanes
94e9335c35
CI: restore opam analysis 2023-10-16 15:45:54 -04:00
Simon Cruanes
33af762216
fix doc 2023-08-31 11:26:56 -04:00
Simon Cruanes
01358f93fd
Merge pull request #437 from BridgeTheMasterBuilder/master
Fix CCMultiMap
2023-08-03 21:53:02 -04:00
BridgeTheMasterBuilder
c97b934542
fix(CCMultiMap): Integrate suggested changes to doc comment for find_right
Co-authored-by: Simon Cruanes <simon.cruanes.2007@m4x.org>
2023-08-03 21:39:39 +00:00
BridgeTheMasterBuilder
85cf52d5ee
fix(CCMultiMap): Integrate suggested changes to doc comment for find_left
Co-authored-by: Simon Cruanes <simon.cruanes.2007@m4x.org>
2023-08-03 21:39:18 +00:00
Master Builder
e0fb678d1e fix(CCMultiMap): Correct @since tags 2023-08-03 15:40:12 +00:00
Master Builder
528b9030a4 fix(CCMultiMap): Rename functions find_left and find_right in the bidirectional multimap to find_left_iter and find_right_iter respectively to reflect their usage, and add new functions to replace the old find_left and find_right that return a list of values rather than an iterator, to make the signatures of CCMultiMap.S and CCMultiMap.BIDIR cohere. Additionally, change the return type of S.find_iter from t -> key -> (value -> unit) -> unit to t -> key -> value iter. These types are the same though, it's just for clarity since CCMultiMap already exposes an iter type 2023-08-03 15:17:47 +00:00
Master Builder
4fb0df50e9 Add missing required dependency to build instructions in Contributing section 2023-08-03 14:31:44 +00:00
Simon Cruanes
ba516e81af
detail 2023-07-12 19:15:23 -04:00
Fardale
d2bdee097e Apply ocamlformat on t_array.ml 2023-07-12 16:55:22 +02:00
Simon Cruanes
492484a9a2
cleanup: remove stubs for code always present on 4.08 2023-07-10 11:55:43 -04:00
Simon Cruanes
61887100ae
deprecate containers.thread 2023-06-28 11:47:02 -04:00
Simon Cruanes
ad10cdc9d5
fix test 2023-06-24 16:05:49 -04:00
Simon Cruanes
979eca042c
remove last refs to CCShims 2023-06-24 15:59:01 -04:00
Simon Cruanes
459098312e
fix opam 2023-06-24 15:46:14 -04:00
Simon Cruanes
9d48d228ef
CI 2023-06-24 15:23:58 -04:00
Simon Cruanes
1a23731730
remove conditional over OCaml >= 4.08
in particular, all the let-ops are now unconditional!
2023-06-24 15:22:21 -04:00
Simon Cruanes
d0903a09be
opam: require OCaml >= 4.08 2023-06-24 15:10:44 -04:00
Simon Cruanes
7ed1d44888
change COC to ocaml-coc 2023-06-20 12:32:50 -04:00
Michael Lan
adda7864e1 doc: fix typo 2023-06-15 21:58:16 -04:00
Simon Cruanes
64eb7737e3
revert change that broke 4.03 2023-06-06 22:55:45 -04:00
Simon Cruanes
bbfbe0f770
fix many, many warnings 2023-06-06 22:16:20 -04:00
Simon Cruanes
77ff1ee6a5
stricter warnings; remove dead code 2023-06-06 22:03:24 -04:00
Simon Cruanes
3975eb9862
fix warnings
reimplement CCtimer's server loop with `Unix.select` because of a
deprecation warning.
2023-06-06 21:54:02 -04:00
Simon Cruanes
afeb2b762a
dune: enable more warnings 2023-06-06 21:53:58 -04:00
Simon Cruanes
81acaaa2cb
prepare for 3.12 2023-06-06 10:26:11 -04:00
Simon Cruanes
e6afa76eaf
fix for OCaml 5.1: use a generative functor for CCBitfield 2023-06-06 10:26:11 -04:00
Simon Cruanes
b72fac90c7
compat: test cannot use let-ops 2023-06-01 15:21:52 -04:00
Simon Cruanes
2b4cf1e663
remove dbg message 2023-06-01 15:03:46 -04:00
Simon Cruanes
9f1ecdba27
another fix for CCParse and slices 2023-06-01 15:03:46 -04:00
Simon Cruanes
c7de9389b0
fix bug in CCParse 2023-06-01 15:03:46 -04:00
Simon Cruanes
10a8a7ce0f
fix(CCParse): fix issue in recurse
recursively parsing a slice means we temporarily switch the state
to the slice; but once the sub-parser returns we need to switch back to
the old state.
2023-06-01 15:03:46 -04:00
Simon Cruanes
6d013251fe
test: add test for CCParse 2023-06-01 15:03:46 -04:00
Simon Cruanes
a8449e9847
Merge pull request #428 from c-cube/wip-pp
pretty printer
2023-06-01 15:03:05 -04:00
Simon Cruanes
cb6c646978
fix small typo in docs 2023-05-28 21:07:29 -04:00
Simon Cruanes
ef40581b44
Merge pull request #430 from bclement-ocp/patch-1
[doc] CCIO.File.walk
2023-05-28 21:07:06 -04:00
Basile Clément
7cf1ba1764
[doc] CCIO.File.walk
This PR clarifies the behavior of `walk` and `read_dir ~recurse:true` by documenting that:

 - `walk p` always includes `p`
 - `read_dir ~recurse:true` only includes file paths (whereas `read_dir ~recurse:false` can include directory paths)
2023-05-26 13:32:47 +02:00
Simon Cruanes
87b10adcca
test: update the cbor tests 2023-04-19 22:11:11 -04:00
Simon Cruanes
74e3a9e875
compat, reformat 2023-04-19 21:22:19 -04:00
Simon Cruanes
c2952e0ce6
pp: add Term_color extension 2023-04-19 21:22:19 -04:00
Simon Cruanes
adaecf470e
pp: add zero-width text; add bracket2 combinator 2023-04-19 21:22:18 -04:00
Simon Cruanes
c1d980048d
more doc 2023-04-19 21:22:18 -04:00
Simon Cruanes
785932861b
helpers for pp 2023-04-19 21:22:18 -04:00
Simon Cruanes
1ed81107df
pp: expose fill and and fill_map 2023-04-19 21:22:17 -04:00
Simon Cruanes
58596a9bd5
chore: makefile 2023-04-19 21:22:16 -04:00
Simon Cruanes
ae7b1aef48
compat 2023-04-19 21:21:47 -04:00
Simon Cruanes
9d35f96033
add Pp.debug 2023-04-19 21:21:46 -04:00
Simon Cruanes
679534597d
fix(pp): slicing needs attention 2023-04-19 21:21:46 -04:00
Simon Cruanes
94640e9efe
comment 2023-04-19 21:21:46 -04:00
Simon Cruanes
91a2ecee4a
Pp: rename wrap to ext; more doc, more combinators 2023-04-19 21:21:46 -04:00
Simon Cruanes
4d77a17029
Pp: expose generic output; simplify extensions 2023-04-19 21:21:45 -04:00
Simon Cruanes
d36c57459e
feat(Pp): add wrap for extension nodes 2023-04-19 21:21:45 -04:00
Simon Cruanes
cea844fdde
add tests for Pp 2023-04-19 21:21:44 -04:00
Simon Cruanes
03c25cb18f
wip: add Containers_pp sub-library
this implements Wadler's pretty printers, albeit in a more eager way.
2023-04-19 21:21:44 -04:00
Simon Cruanes
aa6b40342e
update ocamlformat 2023-04-19 21:21:44 -04:00
Simon Cruanes
c5c72e0a50
move cbor tests 2023-04-19 21:21:43 -04:00
Simon Cruanes
4cf71ef3f0
opam constraint 2023-04-12 09:07:39 -04:00
Fardale
b4c164a8e4 fix: __FUNCTION__ was only introduced in 4.12 2023-04-12 13:56:53 +02:00
Fardale
ff3e838553 CCArray(feat): Add max,argmax,min,argmin and their _exn conterpart 2023-04-12 11:52:57 +02:00
Simon Cruanes
4a46fa4d71
add a op test for Fun_vec 2023-04-09 14:59:39 -04:00
Simon Cruanes
7ec9cd94dc
details 2023-04-09 14:59:32 -04:00
Simon Cruanes
fd760d44a3
makefile 2023-04-09 14:59:19 -04:00
Simon Cruanes
9261e654e7
add Option.flat_map_l 2023-04-07 20:30:43 -04:00
Simon Cruanes
d985019fe1
compat 4.03 2023-04-07 12:27:55 -04:00
Simon Cruanes
84173382db
feat(CCParse): add take_until_success
this reads a slice using the given parser to parse the end delimiter
(e.g "end gpg signature" 😉)
2023-04-07 11:45:12 -04:00
Fardale
ea0e4473a8 fix: fix Set.find_last_map on OCaml 4.03 2023-03-31 12:14:03 +02:00
Fardale
b6d99645ea CCSet: implement find_last_map using find_last
find_last exists since ocaml 4.05, using it for find_map avoid the
linear time behavior on ocaml >= 4.05
2023-03-14 19:17:45 +01:00
Simon Cruanes
0b72812a55
more tests for Vec 2023-03-13 19:34:20 -04:00
Simon Cruanes
503c61f72b
fix(vector): make sure Vector.to_{seq,gen} captures the length initially 2023-03-13 15:37:00 -04:00
Simon Cruanes
83009aac10
feat(cchash): native FNV hash for int64/int32 2023-03-13 15:37:00 -04:00
Fardale
a7b14c5620 doc: inline the stdlib doc of Seq and ListLabels 2023-03-13 11:26:23 +01:00
Fardale
e6611f1920 feat(CCSet): add find_first_map and find_last_map 2023-02-16 12:23:14 +01:00
Fardale
53f2ffca9f CI: fix gh-page
odig need the package to be installed to compile the documentations
2023-02-15 18:31:59 +01:00
Simon Cruanes
1d08a05c44
Merge pull request #426 from FardaleM/cleanup
Cleanup
2023-02-15 09:28:38 -05:00
Fardale
d5d10af079 CI(doc): use odig to generate documentation 2023-02-15 14:32:07 +01:00
Fardale
0a167dc3dd doc: remove link to ocaml manual
The goal is to use odig to build the documentation and with odig the
comment of the function from the standard library are shown.
2023-02-15 14:32:05 +01:00
Fardale
8607de2749 CI(chore): remove explicite pin and deps install
Pinning and installation of the dependency is already done by
setup-ocaml
2023-02-15 11:02:09 +01:00
Fardale
def8f242fc CI(chore): disable dune cache for the doc 2023-02-15 11:01:55 +01:00
Fardale
a30e471a6f
fix doc (#425) 2023-02-14 20:56:15 -05:00
Simon Cruanes
38d6aa4ad1
disable opam dependencies CI action 2023-02-14 20:55:54 -05:00
Simon Cruanes
5ff60d2a52
fix doc 2023-02-14 08:50:56 -05:00
Simon Cruanes
161c192bff
prepare for 3.11 2023-02-07 12:39:35 -05:00
Simon Cruanes
d249ce5f13
fix mdx test 2023-02-07 12:39:35 -05:00
Simon Cruanes
735729c329
add CCFun.(let@) (if OCaml >= 4.08) 2023-02-07 12:23:27 -05:00
Simon Cruanes
b1c39832aa fix(CCVector): concurrent modification safety in resize_with 2023-01-08 22:42:24 -05:00
Simon Cruanes
b73fc4ee5c fix(CCVector): always obtain a copy of array before using unsafe_{set,get}
close #423
2023-01-08 22:42:24 -05:00
Fardale
b0ed8d9182 chore(CI): rename job CI 2022-12-23 15:57:03 +01:00
Fardale
ae16f5d2f8 opam: add mdx as test dependency of containers-data 2022-12-23 14:00:20 +01:00
Fardale
b11aea96bf chore(CI): add ocaml 5.0.x 2022-12-23 13:51:56 +01:00
Samuel Hym
fb09468837 Add OCaml 5 bytecode-only to the tested platforms 2022-12-22 23:08:14 +01:00
Samuel Hym
f58310913a Allow explicit fallback to bytecode for various tests
Relax the requirements for the tests, to fall back on the bytecode
version when the native version is not available. Otherwise the tests
will fail on bytecode-only architectures.
2022-12-22 23:08:14 +01:00
Samuel Hym
1367d5b855 Rewrite a test so that it passes also on the bytecode backend
Rewrite a test to shorten the lexical scope of the string it builds
because, in the bytecode backend, a variable is deemed live at least as
long as its lexical scope.

Reference: https://github.com/ocaml/ocaml/pull/10071
2022-12-22 23:08:14 +01:00
Simon Cruanes
d1de46c7a9
Merge pull request #421 from shym/allow-bytecode-target
Allow explicit fallback to bytecode for cpp
2022-12-12 12:30:15 -05:00
Samuel Hym
5d3768b5a2 Allow explicit fallback to bytecode for cpp
Relax the requirements for the cpp preprocessor, to fall back on the
bytecode version when the native version is not available. Otherwise the
build fails on bytecode-only architectures.
2022-12-12 12:31:04 +01:00
Simon Cruanes
d4e582e829
add CCHet.Tbl.{clear,reset} 2022-11-30 09:21:35 -05:00
Simon Cruanes
cee2c7d8e3
Merge pull request #418 from nino/master
Fix some typos
2022-11-28 11:30:14 -05:00
Nino Annighoefer
46e53ec85f Fix more typos 2022-11-26 10:19:10 +00:00
Nino Annighoefer
3ccb3e16f7 Fix typos 2022-11-26 09:53:17 +00:00
Simon Cruanes
fcfd8f19b0
doc 2022-11-16 13:30:13 -05:00
Simon Cruanes
069423bb77
prepare for 3.10 2022-11-16 10:27:20 -05:00
Simon Cruanes
24fdfdf3ee
readme 2022-11-14 09:08:39 -05:00
Fardale
5227fb975c feat(CCArray): add mapi_inplace 2022-10-19 12:07:13 +02:00
Simon Cruanes
e0a8285e17
add containers.scc 2022-10-05 10:12:50 -04:00
Simon Cruanes
5b1f2af227
chore(github): add opam deps workflow 2022-09-26 11:25:36 -04:00
Simon Cruanes
a0c8859519
list codec libraries in readme 2022-09-21 13:17:45 -04:00
Fardale
399cd6d570 fix(CCSeq): add implementation of concat_map 2022-09-21 16:20:02 +02:00
Fardale
b2ec88b0e1 feat(CCSeq): add some missing function from 4.14 2022-09-21 15:44:18 +02:00
Fardale
46fbc3b82f doc(Unix): add example from call_full 2022-09-21 11:22:02 +02:00
Fardale
47ff9935dc include Seq in CCSeq for ocaml >= 4.07 2022-09-20 15:09:45 +02:00
Fardale
a5f9f2b95d chore(CI): enable dune cache 2022-09-17 21:39:04 +02:00
Fardale
15c9152795 chore(CI): bump the last version of ocaml from 4.13 to 4.14 2022-09-17 21:28:55 +02:00
Simon Cruanes
00d344e09e
fix(Int64.hash): wrong shift
found by @copy
2022-08-29 09:49:51 -04:00
Simon Cruanes
a3abf40bc2 add CCInt64.{hash,hash_to_int64} 2022-08-22 17:48:21 +00:00
Fardale
4e2f9220dd doc: add details to CCResult.of_opt 2022-08-05 21:38:22 +02:00
Simon Cruanes
43f82d7668
feat(Ref): add protect function 2022-08-04 11:58:39 -04:00
Simon Cruanes
d535cfe677
detail 2022-07-21 11:54:46 -04:00
Simon Cruanes
249dc3596e
prepare for 3.9 2022-07-06 22:27:25 -04:00
Simon Cruanes
6a415e963a
fix(cbor): use int64 as main int type 2022-07-06 22:27:24 -04:00
Simon Cruanes
484aa3a1e7
Merge pull request #394 from c-cube/ccbv_bytes2
(continued) use bytes for CCBV
2022-07-06 16:41:21 -04:00
Simon Cruanes
b7d19e9dc5
test 2022-07-05 21:29:03 -04:00
Simon Cruanes
af77f371fd
feat(testlib): allow ?long arg 2022-07-05 21:28:54 -04:00
Simon Cruanes
8b751754ba
test: compat 4.03 2022-07-04 22:16:46 -04:00
Simon Cruanes
feaa8ecf7d
test BV.init 2022-07-04 22:15:13 -04:00
Simon Cruanes
e15971934d
feat(BV): add init 2022-07-04 22:15:06 -04:00
Simon Cruanes
b24feaf2d6
strong BV test 2022-07-04 22:09:25 -04:00
Simon Cruanes
36eb87db21
fix(BV): clear bits properly 2022-07-04 22:09:14 -04:00
Simon Cruanes
e01b758de8
more tests 2022-07-04 22:04:50 -04:00
Simon Cruanes
d9717095ef
improve doc for BV 2022-07-04 22:04:40 -04:00
Simon Cruanes
30cb40c71f
test: add strong tests for BV
we use the classic QCheck construction with a random list of operations, and
test:
- internal invariant after each operation
- same cardinal and content as reference implementation after each operation
2022-07-04 21:56:14 -04:00
Simon Cruanes
60b9ece69e
feat(BV): correct many bugs, clarify parts of the API 2022-07-04 21:49:50 -04:00
Simon Cruanes
75fe196d3a
feat(testlib): optional arguments for q 2022-07-04 21:47:24 -04:00
Simon Cruanes
090945c3f8
fix(BV): equal function 2022-07-04 16:08:17 -04:00
Simon Cruanes
cc55e4cdfb
feat(testlib): optional name for all tests 2022-07-04 16:07:58 -04:00
Simon Cruanes
856e73d2b2
fix 2022-07-04 14:37:24 -04:00
Simon Cruanes
2b5b2a0e02
chore: have make test be quiet 2022-07-04 14:35:32 -04:00
Simon Cruanes
3dd63964fb
Merge branch 'master' into ccbv_bytes2 2022-07-04 14:35:25 -04:00
Simon Cruanes
69f2805f10
Merge pull request #414 from c-cube/wip-testlib
custom testlibrary, remove qtest
2022-07-04 13:57:35 -04:00
Simon Cruanes
8d964458d9 chore: ci 2022-07-04 13:36:06 -04:00
Simon Cruanes
3c5b2329bc tests belong in specific packages 2022-07-04 13:36:06 -04:00
Simon Cruanes
10865eaced reformat 2022-07-04 13:36:06 -04:00
Simon Cruanes
3e2379660e
move cbor tests into tests/ 2022-07-02 23:49:41 -04:00
Simon Cruanes
e242b004ad
use preproc to deal with 4.03 and others < 4.08 versions 2022-07-02 23:39:33 -04:00
Simon Cruanes
919360f96e
remove ounit 2022-07-02 23:18:16 -04:00
Simon Cruanes
55b59b5b91
move more tests into testlib 2022-07-02 22:25:29 -04:00
Simon Cruanes
3b2cd786e2
finish removing qtest 2022-07-02 22:12:40 -04:00
Simon Cruanes
1e4a22fbf2
refactor: finish migration to qtest 2022-07-02 22:09:25 -04:00
Simon Cruanes
1111c0fa9a
wip: convert tests into testlib 2022-07-02 14:47:03 -04:00
Simon Cruanes
91ddccc782
compat 4.03 2022-07-02 01:08:59 -04:00
Simon Cruanes
b695918e99
move all core tests to new testlib 2022-07-02 00:29:25 -04:00
Simon Cruanes
49c06e93fa
wip: use testlib for tests 2022-06-30 22:28:19 -04:00
Simon Cruanes
0bee9bdd55
wip: remove tests from src/ 2022-06-30 22:28:07 -04:00
Simon Cruanes
215c5c7d5b
testlib: improve API 2022-06-30 22:28:01 -04:00
Simon Cruanes
369b208385
test: migrate part of CCList's tests to experiment with testlib 2022-06-30 21:15:16 -04:00
Simon Cruanes
f8d8c0962c
wip: add internal test library to replace qtest 2022-06-30 21:15:16 -04:00
Simon Cruanes
f9abed084e
Merge pull request #413 from c-cube/feat-cbor
add CBOR sub-library
2022-06-30 21:06:58 -04:00
Simon Cruanes
43f88a372f
stronger test for cbor 2022-06-30 21:06:21 -04:00
Simon Cruanes
20fb411b50
compat fix 2022-06-30 20:45:08 -04:00
Simon Cruanes
db9c613f57
add some basic docs for cbor 2022-06-30 20:39:09 -04:00
Simon Cruanes
962874c038
dune 2022-06-30 20:36:14 -04:00
Simon Cruanes
89702924d8
test: add qcheck test for cbor 2022-06-30 20:25:19 -04:00
Simon Cruanes
b1c7c64b87
improve test for cbor 2022-06-30 20:06:38 -04:00
Simon Cruanes
40ceded65f
remove expected file for cbor tests
reason is, under 4.08 we can't produce the same file because CBOR is
basically an empty module.
2022-06-29 20:12:36 -04:00
Simon Cruanes
ddd9ed48f1
chore: disable CI for windows temporarily
it's currently just broken
2022-06-28 23:38:30 -04:00
Simon Cruanes
fd4c679479
disable cbor tests on < 4.08 2022-06-28 23:38:01 -04:00
Simon Cruanes
565b3ed5c2
compat with 4.03 2022-06-28 21:24:53 -04:00
Simon Cruanes
bad23766e3
chore: enable preprocessor in cbor 2022-06-28 20:56:50 -04:00
Simon Cruanes
06b58d36d1
test for cbor, based on appendix A test vector 2022-06-17 22:22:00 -04:00
Simon Cruanes
27ab6af573
depend on yojson for tests 2022-06-17 22:21:59 -04:00
Simon Cruanes
98d0cdfe6d
fix(cbor): many bugfixes 2022-06-17 22:21:56 -04:00
Simon Cruanes
e7b5d675d2
doc: add RFC for CBOR 2022-06-16 22:27:37 -04:00
Simon Cruanes
69907e96d1
feat: add Containers_cbor module
from scratch reimplementation of https://www.rfc-editor.org/rfc/rfc8949.html
2022-06-16 22:21:35 -04:00
Simon Cruanes
e24b2060e7
fix: handle uppercase in string/hex 2022-06-15 13:38:43 -04:00
Simon Cruanes
75b498a433
prepare for 3.8 2022-06-10 23:47:21 -04:00
Simon Cruanes
259edb965b
try to fix test 2022-06-10 23:26:21 -04:00
Simon Cruanes
90a131b7af
fix compiler warning 2022-06-10 23:25:22 -04:00
Simon Cruanes
58ac755f82
chore: move to ounit2
see if that fixes the windows build.
2022-06-09 21:39:09 -04:00
Simon Cruanes
977e0c9577
add test for String.to_hex 2022-06-09 21:11:47 -04:00
Simon Cruanes
911e9faff7
add Containers_bencode
A small module to parse/print Bencode values. Bencode is really simple
and can embed binary strings easily, unlike JSON.
2022-06-09 21:07:43 -04:00
Simon Cruanes
e63383174e
perf(cchash): improve a bit commutative hashing of arrays/lists 2022-06-07 16:20:26 -04:00
Simon Cruanes
40133ee511
perf(cchash): only hash prefix of string/bytes 2022-06-07 16:20:14 -04:00
Simon Cruanes
e7dad1b54a
fix(parse): rename function in test case
like Grant remarked, the chaining function is actually
right-associative, not left-associative
2022-06-07 16:19:28 -04:00
Simon Cruanes
322b15d757
Merge pull request #409 from dmbaturin/assoc-helpers
New assoc list helpers: `keys`, `values`, and `map_values`
2022-06-04 21:15:21 -04:00
Daniil Baturin
0075378f29 feat(CCList): Add keys, values, and map_values 2022-06-01 15:49:33 +03:00
Fardale
70703b3512 fix: add since tag for Array.map_inplace 2022-05-12 11:19:47 +02:00
Fardale
8a71b1dcaa feat(ccarray): add CCArray.map_inplace 2022-05-12 11:03:38 +02:00
Simon Cruanes
e59cc68c24
fix doc 2022-05-09 10:49:48 -04:00
Simon Cruanes
2e4db82b67
doc: readme irc 2022-04-28 21:24:44 -04:00
Simon Cruanes
522772356f
chore: CI 2022-04-16 23:12:32 -04:00
Simon Cruanes
34f76e926c
add CCString.{to_hex,of_hex} 2022-04-06 10:53:26 -04:00
Simon Cruanes
a753b0df3e
Merge remote-tracking branch 'origin/master' 2022-03-24 15:46:23 -04:00
Simon Cruanes
a2a1901630
prepare for 3.7 2022-03-24 13:46:45 -04:00
Simon Cruanes
54201a4e28
fix(atomic): prevent race conditions under flambda, for now 2022-03-24 13:42:24 -04:00
Glenn Slotte
7f4c87cfb9
docs: fix CCOption.map_or doc comment (#407) 2022-03-23 09:02:53 -04:00
Simon Cruanes
6fa4c1c7d2
Merge pull request #406 from c-cube/wip-format-stag
use `stag` for color handling in CCFormat
2022-03-19 18:53:43 -04:00
Simon Cruanes
5a4adfa76b
fixes, do not run Format tests on < 4.08 2022-03-19 14:13:15 -04:00
Simon Cruanes
0ce613d7c4
gate more code in the version conditional 2022-03-19 13:53:38 -04:00
Simon Cruanes
38552f5c0c
use stag properly, add with_styling.
all tests pass again.
2022-03-19 10:06:08 -04:00
Simon Cruanes
e397d90279
wip: use Stag in Format 2022-03-19 09:40:43 -04:00
Simon Cruanes
40189757ca
Merge pull request #401 from c-cube/experiment-preproc
custom preprocessor rather than shim modules
2022-02-22 23:15:56 -05:00
Simon Cruanes
1b92e905e4
chore: rename build on CI 2022-02-22 23:09:50 -05:00
Simon Cruanes
e9e959eb6c
small optim in preprocessor 2022-02-22 11:26:46 -05:00
Simon Cruanes
0364929a99
improve test behavior 2022-02-22 11:26:43 -05:00
Simon Cruanes
f6dc3b23f8
fix: compat in CCArray 2022-02-21 22:26:51 -05:00
Simon Cruanes
68e539173f
failfast in main build task 2022-02-21 22:26:44 -05:00
Simon Cruanes
ceebfe3ae1
update CI with distinct jobs 2022-02-21 22:18:30 -05:00
Simon Cruanes
558c069b7b
forgotten module 2022-02-21 22:03:40 -05:00
Simon Cruanes
dae93cf25f
remove final shims 2022-02-21 22:03:26 -05:00
Simon Cruanes
b837509de9
remove many more shims 2022-02-21 21:52:06 -05:00
Simon Cruanes
26ab8229e1
preprocess monomorphic too 2022-02-21 21:26:49 -05:00
Simon Cruanes
6f3a7d902a
remove more shims 2022-02-21 21:25:39 -05:00
Simon Cruanes
c32529fd5a
ci: run a simple build matrix first 2022-02-21 21:20:30 -05:00
Simon Cruanes
59407b0f5e
wip: remove some shims 2022-02-21 17:12:32 -05:00
Simon Cruanes
3d87d2672e
ci 😱 2022-02-21 16:47:55 -05:00
Simon Cruanes
eadfa4981a
force dune 2.9 in CI 2022-02-21 15:31:17 -05:00
Simon Cruanes
5840d677c0
Merge pull request #402 from bluddy/master
CCVector: add `insert`
2022-02-21 14:47:58 -05:00
Yotam Barnoy
408c14fac7
CCVector.insert: check if there's a need to blit
Co-authored-by: Simon Cruanes <simon.cruanes.2007@m4x.org>
2022-02-21 18:50:19 +02:00
Yotam Barnoy
9bb280e353 CCVector: add insert 2022-02-21 18:42:16 +02:00
Simon Cruanes
ef9851983f
update dune to 1.10, condition some rules to unix 2022-02-20 22:01:39 -05:00
Simon Cruanes
b23e075762
fix occurrences of warning 50 2022-02-20 22:01:02 -05:00
Simon Cruanes
6717d03a35
remove custom split_on_char post 4.04 2022-02-17 14:37:14 -05:00
Simon Cruanes
01295a71fd
grr \r on windows 2022-02-17 10:46:34 -05:00
Simon Cruanes
8aa50b2523
dune 2 shenanigans 2022-02-17 10:34:08 -05:00
Simon Cruanes
c50ee3d928
try to fix compat issue 2022-02-17 10:20:24 -05:00
Simon Cruanes
acadb6b9d3
move to dune 2.0 2022-02-17 10:17:08 -05:00
Simon Cruanes
4934b302c6
feat(cpp): better locations after blocks end 2022-02-17 10:16:11 -05:00
Simon Cruanes
10286098c4
fix qtest generation
we need to avoid files named foo.pp.ml as they're not handled by qtest
the right way (computes the wrong module name).
2022-02-17 00:17:37 -05:00
Simon Cruanes
60a1614919
silence warning 70 2022-02-17 00:17:31 -05:00
Simon Cruanes
7ae113b6dc
compat with merlin by using [@@@ifge 4.12] instead of [%IFGE 4.12] 2022-02-16 23:14:26 -05:00
Simon Cruanes
bc6c8947b1
start using preprocessor to remove some shim modules 2022-02-16 23:01:00 -05:00
Simon Cruanes
2d860b30ae
fix 2022-02-16 22:45:10 -05:00
Simon Cruanes
7bd0aa075c
wip: try to have a custom little preprocessor 2022-02-12 20:22:52 -05:00
Simon Cruanes
45f567dca1
perf: reduce allocations in CCSeq.to_array 2022-02-11 21:15:09 -05:00
Simon Cruanes
2a1c7cd8f0
fix warning 2022-02-11 21:15:04 -05:00
Simon Cruanes
01b209b218
doc: fix mdx for readme 2022-02-11 21:14:51 -05:00
Simon Cruanes
c5d435848b
improve test for CCByte_buffer 2022-02-08 13:07:51 -05:00
Simon Cruanes
826381690c
add random test to CCByte_buffer 2022-02-08 13:07:51 -05:00
Simon Cruanes
d7214345e5
update doc and aliases 2022-02-08 13:07:51 -05:00
Simon Cruanes
b42b1f4907
fix: rename size to cap in CCByte_buffer 2022-02-08 13:07:50 -05:00
Simon Cruanes
5f064dbbbf
feat: add Byte_buf, a byte buffer. 2022-02-08 13:07:50 -05:00
Simon Cruanes
bc8b7b168b
Merge pull request #399 from c-cube/wip-ccvector-resize
change growth strategy for Vector
2022-02-07 12:46:48 -05:00
Simon Cruanes
21c10d2ad4
less aggressive constant.
series of sizes should now be:

```
 # let next n = n + n lsr 1 + 2;;
 # CCSeq.unfold (fun x -> Some (x, next x)) 0 |> CCSeq.take 20 |> CCSeq.to_list;;
- : int list =
[0; 2; 5; 9; 15; 24; 38; 59; 90; 137; 207; 312; 470; 707; 1062; 1595; 2394;
 3593; 5391; 8088]
 ```
2022-02-03 19:15:13 -05:00
Simon Cruanes
02c0953468
fix stupid typo 2022-02-03 19:13:43 -05:00
Simon Cruanes
0baa4fddec
perf(vector): inline some more stuff plz 2022-02-03 18:55:50 -05:00
Simon Cruanes
8c9d7016b8
perf: uniformize ocamlopt_flags
we don't need codegen which depends on >= 4.03, it's assumed. Also
include a reasonable value for `-inline` when flambda isn't available.
2022-02-03 18:54:59 -05:00
Simon Cruanes
7ec9e50f74
perf: make sure to use some decent level of inlining even without flambda 2022-02-03 18:51:09 -05:00
Simon Cruanes
f540a6d7e5
perf(vector): less aggressive growth for internal resizing 2022-02-03 18:50:32 -05:00
Simon Cruanes
2d30b2ae14
add CCSeq.{zip_i,of_string} 2022-01-27 13:48:49 -05:00
Simon Cruanes
e25b9fc9b4
fix @since in CCSeq 2022-01-27 13:45:01 -05:00
Ewen Maclean
4e79b72306
adding opt_map to simplify result function application over optionals (#397)
adding `Result.opt_map` to simplify result function application over optionals
2022-01-25 15:05:11 -05:00
Fardale
a13fc12ff4 chore(ci): change ocaml 4.12.x to ocaml 4.13.x 2022-01-22 21:10:52 +01:00
Simon Cruanes
3960ea3792
feat(BV): add set_bool 2022-01-02 21:58:45 -05:00
Simon Cruanes
3d57a5c86e
feat(CCBV): prevent resize from shrinking underlying array
also add `BV.resize_minimize_memory` to force shrinking. This shouldn't
be the default because it can allocate a lot in case of repeated
shrinkings.
2022-01-02 21:57:53 -05:00
Simon Cruanes
92463d33c5
perf(BV): make more functions inline, use raise_notrace 2022-01-02 21:56:33 -05:00
Simon Cruanes
ced66a76e1
perf(CCBV): better bitwise operations
- a 8-bit popcount
- simpler logic for LSB masks
2022-01-02 21:45:26 -05:00
Simon Cruanes
b8c93f42fa
feat(CCInt32): add popcount function 2022-01-02 21:44:05 -05:00
Simon Cruanes
22bbe23c5a
feat(CCInt64): add popcount operation
adapted from CCInt, but directly on int64, so it works for
Int64.{min_int/max_int}.
2022-01-02 21:17:18 -05:00
Simon Cruanes
569e254540
fix: use == 2022-01-02 12:15:31 -05:00
Simon Cruanes
64ecd0c3ba
add shims for Atomic and Unit 2022-01-02 12:08:16 -05:00
Simon Cruanes
4e0f35c078
make fun shims inline 2021-12-22 11:00:33 -05:00
Jochen Bartl
ab0673a688
fix typos in README (#393) 2021-12-20 18:12:11 -05:00
Simon Cruanes
a127e139ae
doc for CCParse
close #392
2021-12-16 13:17:09 -05:00
Shon Feder
946ac4e05d
Make CCSeq.to_array behave better with stateful sequences (#390)
Make Seq.to_array only traverse seq once

This PR suggests a change to `Seq.to_array`, which uses construction of
an intermediate list to prevent traversing the Seq twice. This requires
the allocation of an intermediate list, but it eliminates the surprising
behavior that otherwise occurs with state-full sequences, due to the
extra traversal required to obtain the length of the sequence. E.g.,
with the previous implementation, the value of `to_array` for the
sequence constructed in the test added in this commit is `[|4;5;6|]`,
while `to_list` gives `[|1;2;3|]`.
2021-12-11 21:28:10 -05:00
Simon Cruanes
74954f53a0
update changelog 2021-12-04 14:37:21 -05:00
Simon Cruanes
16bea66073
more warnings 2021-12-02 11:48:57 -05:00
Simon Cruanes
cea6647c3c
fix warnigns for CCOption 2021-12-02 11:46:41 -05:00
Simon Cruanes
bee23722ea
fix: use labels in CCParse 2021-12-02 11:38:22 -05:00
Simon Cruanes
9123f7907f
feat(ccparse): expose pos to get current pos; improve perf
perf of obtaining many positions is now better because we cache line
offsets, which means computing a line,col pair is just a O(ln n) bisect
away.
2021-12-01 16:05:20 -05:00
Fardale
c33477c397 chore: remove unwanted comment 2021-11-12 17:38:37 +01:00
Fardale
a5822f7273 doc: inline the doc of CCShim*_ module in the doc 2021-11-12 17:34:06 +01:00
Fardale
57cb3446b3 add semicolon to ocaml block in the README 2021-11-12 11:42:54 +01:00
Fardale
099f2e176f chore(doc): adapt module docstring for the index page 2021-11-11 14:40:35 +01:00
Simon Cruanes
db1ebaf3ce
Merge branch 'wip-3.6.1' 2021-10-25 20:13:17 -04:00
Simon Cruanes
04693f4f08
fix opam file 2021-10-25 09:35:57 -04:00
Simon Cruanes
d59a856787
prepare for 3.6.1 2021-10-24 22:57:54 -04:00
Simon Cruanes
62ee8ad17e
fix: rely on either compatibility library 2021-10-24 22:57:17 -04:00
Simon Cruanes
2100a0a0fb
fix: rely on either compatibility library 2021-10-24 22:56:36 -04:00
Simon Cruanes
2c2fa5d008
fix asymptotic behavior of resize functions 2021-10-21 11:07:11 -04:00
Simon Cruanes
e6e07ba4da
fix tests and implem for CCVector 2021-10-21 10:59:03 -04:00
Dario Pinto
d1ddeeb31f
add CCVector.resize_with and CCVector.resize_with_init, tests and doc (#389)
add CCVector.resize_with and CCVector.resize_with_init, tests and doc
2021-10-21 10:57:23 -04:00
Simon Cruanes
541d716d5c
in CCVector, use invalid_arg, and document it 2021-10-20 10:47:06 -04:00
Simon Cruanes
7288045828
more doc for CCVector 2021-10-19 09:34:45 -04:00
Simon Cruanes
b2cff1d0b7
prepare for 3.6 2021-10-18 22:31:44 -04:00
Simon Cruanes
2c7e907061
update benchs 2021-09-27 20:43:16 -04:00
Fardale
475e7b181e chore(ci): use new syntax for minor ocaml version in setup-ocaml 2021-09-27 13:07:50 +02:00
Fardale
2a7bc70bed chore(ci): migrate gh-pages workflow to setup-ocaml@v2 2021-09-27 13:07:50 +02:00
Fardale
800fdf4d5e chore(ci): bump version from 4.12.0 to 4.12.1 2021-09-27 13:07:50 +02:00
Fardale
3ae5699021 chore(CI): fix ocaml compiler version 2021-09-27 13:07:50 +02:00
Simon Cruanes
bf15e88f0c
fix doc 2021-09-25 20:58:31 -04:00
Simon Cruanes
ff2d1d3cbc
Merge pull request #380 from c-cube/wip-parse-2021-05-04
refactor `CCParse` to make it easier to use
2021-09-25 20:57:26 -04:00
Simon Cruanes
9c72797515
minor changes 2021-09-25 20:51:18 -04:00
Simon Cruanes
938c7cb90a
more doc 2021-09-25 20:50:04 -04:00
Simon Cruanes
b8fa400465
a test to ensure chars1_if p = take1_if p >|= Slice.to_string 2021-09-25 20:48:23 -04:00
Simon Cruanes
6eb8856957
Merge pull request #386 from c-cube/rename_ccopt_ccoption
Rename CCOpt to CCOption and deprecate CCOpt
2021-09-25 15:49:00 -04:00
Simon Cruanes
e06cd516f0
detail 2021-09-25 15:38:35 -04:00
Simon Cruanes
396a7db967
more fixes 2021-09-24 22:58:08 -04:00
Simon Cruanes
8f9ecf5f41
some fixes related to review 2021-09-24 22:45:17 -04:00
Simon Cruanes
ac1baae839
add missing @since 2021-09-24 14:37:36 -04:00
Simon Cruanes
16576e8838
Update src/core/CCOpt.mli 2021-09-24 14:36:53 -04:00
Fardale
302dba6cb5 chore(CCOption): rename CCOpt to CCOption and deprecate CCOpt
In the stdlib the module associated with the option type is called
Option and in containers it was called CCOpt. Renaming CCOpt to CCOption
make the name of containers module uniforme with respect to the stdlib.
2021-09-22 23:07:05 +02:00
Fardale
27e39a0fc8 chore: remplace which' by command -v'
Using `which' to test the existance of a program is deprecated in
debian. Debian recommand to use `command -v' instead.
2021-09-20 14:15:44 +02:00
Fabian
fd783336b8 remove duplicate :standard in dune 2021-08-28 15:59:33 -04:00
Simon Cruanes
f5409d480a
chore: remove dead makefile target 2021-08-26 10:07:48 -04:00
Simon Cruanes
76b108203a
add iterator functions to CCIO 2021-08-19 10:23:00 -04:00
Simon Cruanes
6b99433716
bugfix in CCIO 2021-08-19 10:21:07 -04:00
Simon Cruanes
8e924c98be
add CCIO.File.walk_iter 2021-08-19 10:14:30 -04:00
Simon Cruanes
4783c635fd remove dead doc files 2021-08-09 13:26:30 -04:00
Simon Cruanes
824dfb427c chore: use standard format for license 2021-08-05 10:28:09 -04:00
Simon Cruanes
aa05f69471 prepare for 3.5 2021-08-04 16:49:29 -04:00
Simon Cruanes
30419a2ec7 chore: try to move to setup-ocaml v2 2021-08-02 14:08:00 -04:00
Simon Cruanes
1b87075284 fix test for multicore 2021-08-02 13:32:09 -04:00
Simon Cruanes
f5505297de add CCHash.map 2021-07-09 14:40:15 -04:00
Simon Cruanes
4db9d4eccb style 2021-06-26 23:51:06 -04:00
Simon Cruanes
61b9762269 feat(CCIO): add many Seq.t based functions
each generator function can now produce a seq.
2021-06-26 23:50:30 -04:00
Simon Cruanes
26af1f1297 feat(ccutf8string): add {make,empty,of_uchar} 2021-06-23 14:05:10 -04:00
Simon Cruanes
92aad159c8 add CCFormat.{const_string,opaque} 2021-06-23 14:05:10 -04:00
Simon Cruanes
25660ee2c1 add CCOpt.{some,none}
close #382
2021-06-19 18:43:17 -04:00
Simon Cruanes
1f92564f83 try to fix build 2021-06-08 21:43:17 -04:00
Simon Cruanes
f1084c9b9e test: add some property tests on Csexp/Canonical_sexp 2021-06-08 18:25:46 -04:00
Simon Cruanes
b9828375e1 use dune 1.4 and build ccparse_irclogs only on >= 4.08 2021-06-07 00:28:35 -04:00
Simon Cruanes
5c67fb51ab try to fix ci 2021-06-07 00:10:20 -04:00
Simon Cruanes
1450b869f9 try to fix ci 2021-06-06 23:23:46 -04:00
Simon Cruanes
9c763991ef add example and test of an IRC log parser
also add a sample of IRC logs of #ocaml on libera.chat, to make sure
they parse properly.
2021-06-06 23:01:50 -04:00
Simon Cruanes
d46a679b3b fix bug in CCParse.line, add set_current_slice, fix tests 2021-06-06 23:00:51 -04:00
Simon Cruanes
352fc10d3b more doc for CCParse 2021-06-06 22:49:43 -04:00
Simon Cruanes
1517f64f55 CCParse: add slice and the ability to recurse on them
the idea is that it's often convenient to split the input into smaller
part (e.g. lines), or do a first pass of parsing that just returns a
slice of the input; and then later to use another parser on that slice
to extract the actual data. The new notion of `slice` allows that,
while preserving locations wrt the original input.
2021-06-06 22:42:20 -04:00
Simon Cruanes
7bdc3cff24 add example CCParse-based Sexpr parser, and a test 2021-06-06 18:50:28 -04:00
Simon Cruanes
88fe234a4c add CCParse.{char_fold, chars_fold_map}
useful for non-trivial lexing
2021-06-06 18:49:55 -04:00
Simon Cruanes
c63a2b7b37 fix tests: use dune's locks with absolute path 2021-06-06 18:49:36 -04:00
Simon Cruanes
294fce8634 fixup! feat(ord): add poly, deprecate compare 2021-06-06 17:39:09 -04:00
Simon Cruanes
7081a411c8 small doc change 2021-06-06 17:16:26 -04:00
Simon Cruanes
78a530ccee feat(ord): add poly, deprecate compare 2021-06-06 17:15:51 -04:00
Simon Cruanes
c10ae8d84f parse: fix bugs, add tests, add U.{in_paren,in_paren_opts,option} 2021-06-06 17:08:57 -04:00
Simon Cruanes
37af485971 parse: expose Position module, add or_, both, lookahead, U.bool 2021-06-06 15:08:13 -04:00
Simon Cruanes
171b4ddcd9 parse: deprecate try_, rename new function try_opt 2021-06-06 14:14:48 -04:00
Simon Cruanes
0ec40c2331 CCParse: heavy refactoring, many new functions 2021-06-06 14:14:48 -04:00
Simon Cruanes
7318162c55 wip: rework CCParse 2021-06-06 14:14:48 -04:00
Simon Cruanes
40a6c17548 small changes in CCIntMap, some cleanup 2021-06-06 14:14:05 -04:00
Simon Cruanes
95e96fb5e1 feat(CCFormat): expose ANSI_codes module 2021-06-04 15:43:59 -04:00
Simon Cruanes
ba638aeb70 makea benchs sohuld use profile=release 2021-06-04 10:21:22 -04:00
Simon Cruanes
57e810a882 Revert "small changes related to docs of sorted_diff_uniq"
This reverts commit f7a2edae25.
2021-05-25 19:19:48 -04:00
Simon Cruanes
f7a2edae25
small changes related to docs of sorted_diff_uniq 2021-05-25 19:16:37 -04:00
favonia
1c6bc16362 style(list): move sorted_mem up 2021-05-24 09:26:23 -05:00
favonia
8d532f9a00 feat(list): add sorted_diff_uniq
Also fixed documentation
2021-05-24 09:26:08 -05:00
Fardale
19c65b5472 apply comments from the review 2021-05-23 15:01:36 +02:00
Fardale
6a3e446d27 use precomputed table for count_bits_ 2021-05-23 14:49:31 +02:00
favonia
80e403c969 feat(list): add sorted_mem 2021-05-23 00:45:18 -05:00
favonia
e58c5d8f3b fix(list): add the test sorted_diff (sorted_merge l1 l2) l2 = l1
Also fixed a typo.
2021-05-22 21:41:36 -05:00
favonia
c030beaf52 fix(list): support 4.03 (using CCList.init) 2021-05-22 21:29:21 -05:00
favonia
6d2dc4ccf4 feat(list): add the optional argument all to sorted_remove
Also added another missing "since".
2021-05-22 21:27:35 -05:00
favonia
f6829d1219 fix(list): add "since" for sorted_diff
Co-authored-by: Simon Cruanes <simon.cruanes.2007@m4x.org>
2021-05-22 21:06:20 -05:00
favonia
130f0a63bb feat(list): add sorted_diff 2021-05-22 21:02:15 -05:00
favonia
de7f445207 fix(list): support 4.03 2021-05-22 20:43:35 -05:00
favonia
8c197da02c feat(list): add sorted_remove 2021-05-22 20:28:45 -05:00
Fardale
8ff253f18d chore(CCBV): clean comments 2021-05-22 22:09:24 +02:00
Fardale
8f65bf639b use bytes instead of int array for CCBV 2021-05-22 21:38:36 +02:00
Fardale
3eb676c55c replace for loop and unsafe_get by using iter 2021-05-19 23:36:00 +02:00
favonia
e3b0600a8b fix(hash): resolve ambiguous doc comment of bytes 2021-05-18 17:53:23 -04:00
Simon Cruanes
f352ca916d add a bit of doc 2021-05-18 11:11:31 -04:00
Simon Cruanes
c286bb6d4e Update src/core/CCHash.mli 2021-05-18 10:54:44 -04:00
favonia
13429e5e88 feat(hash): add bytes 2021-05-18 10:54:44 -04:00
Simon Cruanes
795ae5c546 bv: also refactor inter 2021-05-17 10:03:58 -04:00
Simon Cruanes
1c8265c3f3 bv: refactor for performance and readability 2021-05-17 10:01:35 -04:00
Simon Cruanes
92519b4843 fix(bv): index error in union
close #370
2021-05-17 10:00:59 -04:00
Simon Cruanes
a642aa6f6b bv: add more tests, including regression for #370 2021-05-17 10:00:32 -04:00
Simon Cruanes
1b0639886d feat(bv): add equal 2021-05-17 10:00:05 -04:00
Simon Cruanes
6ace6f71e0 doc: fix wrong @since 2021-05-10 11:26:53 -04:00
Simon Cruanes
1167ffdb3c relax constraints in opam 2021-05-04 08:54:08 -04:00
Simon Cruanes
6bfd7f125e test: fix too broad test 2021-05-04 08:54:08 -04:00
Simon Cruanes
943ce7f734 get ready for 3.4 2021-05-03 16:56:24 -04:00
Simon Cruanes
c99f7818c3 udpate doc and add test 2021-04-27 13:21:34 -04:00
Daniil Baturin
3628feed9c Add CCOpt.get_exn_or and deprecate CCOpt.get_exn 2021-04-27 13:15:51 -04:00
Simon Cruanes
0d9a3b82fa
Merge pull request #365 from jberdine/gar
feat(CCRAL): add `get_and_remove_exn` operation
2021-04-16 10:51:17 -04:00
Josh Berdine
430e2a4951 test: ensure tests are run by disabling dune cache 2021-04-16 10:45:28 -04:00
Josh Berdine
9211a01f35 code review 2021-04-16 15:15:11 +01:00
Josh Berdine
9e6f453aff feat(CCRAL): add get_and_remove_exn operation
It seems like a waste to repeat the search for an index to both get
the element at that index and then to remove it. The added
`get_and_remove_exn` operation performs a `remove` but returns the
found element rather than forgetting it.
2021-04-16 14:48:14 +01:00
Josh Berdine
0ab8597b78 fix: CCRAL.remove does not remove 2021-04-15 17:56:36 -04:00
Simon Cruanes
b19cd0db5f add regression test for #364 2021-04-15 17:54:18 -04:00
Simon Cruanes
5611cbf7f3 fix(sexp): re-export the loc type to the functor's argument's type 2021-04-15 10:34:27 -04:00
Fardale
375ae27622 feat(CCString): add CCString.uniq
CCString.uniq remove consecutive duplicate characters
2021-04-08 23:09:53 +02:00
Simon Cruanes
e75d93bb9d refactor and clarify cutoff in String.edit_distance 2021-04-08 11:20:54 -04:00
Simon Cruanes
45b3956421 improve test hash 2021-04-05 00:13:33 -04:00
Fardale
8d6c7470eb Revert "ci: test containers-data on windows and macos"
This reverts commit 75a2f8a325.
2021-04-04 23:39:48 +02:00
Fardale
72b25cfa29 Revert "ci: install test dependency for containers-data on windows and macos"
This reverts commit ca3ca3aaff.
2021-04-04 23:39:37 +02:00
Fardale
ca3ca3aaff ci: install test dependency for containers-data on windows and macos 2021-04-04 23:32:46 +02:00
Fardale
75a2f8a325 ci: test containers-data on windows and macos 2021-04-04 23:23:46 +02:00
Simon Cruanes
25c5eda528 doc: more docs for codegen 2021-04-03 18:54:07 -04:00
Simon Cruanes
4f68b0fc37 fix test for old ocaml 2021-04-03 18:12:03 -04:00
Simon Cruanes
2440092eb5 fix invalid test in intmap 2021-04-03 17:42:17 -04:00
Simon Cruanes
77e3e97dd0 perf: direct alias in CCHash.int 2021-04-03 17:42:17 -04:00
Simon Cruanes
93c0a9af0d fix qtest generation 2021-04-03 17:42:17 -04:00
Simon Cruanes
6323bdc6d3 fix test in CCHash 2021-04-03 17:42:17 -04:00
Simon Cruanes
5bd031c3c2 add test executable for hash functions 2021-04-03 17:42:17 -04:00
Simon Cruanes
cf0d044407 refactor CCHash to use FNV in many combinators 2021-04-03 17:42:17 -04:00
Simon Cruanes
238123b955 move uniformity tests out of CCRandom 2021-04-03 17:42:17 -04:00
Simon Cruanes
7717cc13db fix(ccint): make sure hash is always positive 2021-04-03 17:42:17 -04:00
Simon Cruanes
cc7799f379 perf(ccint): single implementation of popcount using int64 2021-04-03 17:42:17 -04:00
Simon Cruanes
ec796d5fc5 perf(int): use FNV for integer hashing 2021-04-03 17:42:17 -04:00
Fardale
79bbb5ce33 ci: fix cache key for gh-pages 2021-04-03 20:27:44 +02:00
Fardale
701a558676 ci: fix cache for gh-pages job 2021-04-03 20:25:54 +02:00
Fardale
b2342ead0a ci: update ocaml version for gh-pages 2021-04-03 16:41:54 +02:00
Fardale
957bbb10d2 readme: update status badge 2021-04-03 16:38:38 +02:00
Fardale
c7b4c0d0de ci: limit test to 1 process for windows and macos 2021-04-03 12:37:11 +02:00
Fardale
02224148c6 ci: fix name of the main branch 2021-04-02 21:25:08 +02:00
Fardale
8e1e4d36ed ci: fix caching and other improvement 2021-04-02 21:23:00 +02:00
Simon Cruanes
739dd4412c prepare for 3.3 2021-04-01 22:16:40 -04:00
Bertrand Bonnefoy-Claudet
13028c3d17 CCSeq: Add for_all and exists
The functions are implemented the same way as in `oseq` and their
documentation is inspired from their counterparts in `Stdlib.List`.
2021-03-29 18:16:44 -04:00
Simon Cruanes
bfaffc5c39 refactor: have bench compile again 2021-03-28 18:15:45 -04:00
Simon Cruanes
f41887c367 refactor(pool): less locking, fix deadlock, more parallelism 2021-03-28 18:15:45 -04:00
Simon Cruanes
40c05cc7e3 wip: feat(pool): keep one idle thread
see #360; in combination with max_size=1 it means the pool contains
exactly one thread.
2021-03-28 18:15:45 -04:00
Simon Cruanes
8982f87ca7 perf: small optim in Pool.sequence_a 2021-03-28 18:15:45 -04:00
Simon Cruanes
dd1cf2a046 perf(pool): try to inline with_lock 2021-03-28 18:15:45 -04:00
Simon Cruanes
178f7dc92f feat(sexp): expose last location in decoder 2021-03-25 15:20:51 -04:00
Matt Bray
0a54024143 doc: clarify CCOpt.filter 2021-03-19 10:37:28 -04:00
Simon Cruanes
0de515b94b attempt to fix tests 2021-03-13 16:56:21 -05:00
Fardale
2c96dd1b55 feat(CCChar): add CCChar.Infix 2021-03-12 18:27:53 +01:00
Fardale
4ad331fbe3 feat(CCString): add CCString.foldi 2021-03-12 18:13:29 +01:00
Simon Cruanes
5ad8914e4c feat: add code-generator for optimal bitfields; add tests 2021-03-02 09:40:42 -05:00
Simon Cruanes
5593e28431 feat(CCFormat): add string_lines combinator 2021-02-26 17:25:33 -05:00
Simon Cruanes
89d6feed98 fix 2021-02-25 12:59:56 -05:00
Fardale
1975c98025 fix: wrong name in 6b52ec69
partition_filter_either -> partition_map_either
2021-02-25 18:51:33 +01:00
Fardale
6b52ec6945 feat(CCList): update with regards to partition_map
- Add partition_filter_map
- Deprecate partition_map
- Add partition_map_either that match the partition_map from the std
2021-02-25 17:31:15 +01:00
Simon Cruanes
30121b8d2c
Merge pull request #358 from copy/master
Fix integer overflow warning on jsoo (#346)
2021-02-22 20:56:41 -05:00
Fabian
dcf1b4aa6c Fix integer overflow warning on jsoo (#346)
- Remove popcount from shims
- Express large integer literals using bitshifts
2021-02-21 15:53:54 -05:00
Simon Cruanes
b5ecb273ef feat: add CCList.cons'
close #354
2021-02-12 14:35:29 -05:00
Simon Cruanes
2f7366be59
Merge pull request #351 from darrenldl/add-fuzz-helper-scripts
Small upgrade to fuzzing suite scripts
2021-02-12 10:54:54 -05:00
Simon Cruanes
01f70cc802 update code and comments 2021-02-07 13:03:53 -05:00
Arnaud Spiwack
85decd732c
CCMap: implement {of,add}_*_with family of function with update (#352)
This is comparable in conciseness and clarity as an explicit try/with
but it paves the way for a more efficient implementation using the
`update` from the Stdlib which, I presume, uses a one-pass algorithm.
2021-02-07 13:03:33 -05:00
Simon Cruanes
3bee276028 try to fix stupid CI 2021-02-06 12:22:20 -05:00
Simon Cruanes
0ef515f1af feat: add CCHashtbl.{of,add}_{list,seq,iter}_with 2021-02-06 12:16:03 -05:00
Simon Cruanes
51bb9175f3 add CCMap.of_{list,iter,seq}_with functions 2021-02-06 11:50:41 -05:00
Fardale
c4aabbf699 doc: fix doc in CCParse 2021-02-05 15:25:25 +01:00
Darren Ldl
d276208c81 Updated fuzz/run.sh with an upgraded version 2021-02-05 15:29:06 +11:00
Darren Ldl
8efa095a11 Updated .gitignore to ignore fuzz-logs/ 2021-02-05 15:28:45 +11:00
Darren Ldl
5de909a534 Copied fuzzing helper scripts over 2021-02-05 15:20:55 +11:00
Simon Cruanes
447df826f1 move canonical sexps into their own module 2021-02-02 23:02:54 -05:00
Simon Cruanes
176b2e5ff2 cleanup of makefile 2021-02-02 22:48:16 -05:00
Simon Cruanes
427c15e472 add fuzzing for csexp 2021-02-02 22:48:08 -05:00
Simon Cruanes
b2b1d2b5fa feat(ccsexp): printer and parser for canonical S-exprs 2021-02-02 22:16:15 -05:00
Simon Cruanes
0097fd3c3d prepare for 3.2 2021-02-01 10:40:28 -05:00
Fardale
f313361df7 try to fix CI for gh-pages 2021-01-26 12:03:12 +01:00
Fardale
5520735a77 fix: enable jekyll on gh-pages 2021-01-26 11:55:50 +01:00
Fardale
179d19e444 fix(CI): correct the name of the main branch 2021-01-26 11:43:07 +01:00
Fardale
b34c3fe75a try to enable auto deploy of doc 2021-01-26 11:41:44 +01:00
Fardale
d9df726ca0 clean: remove stuff specific for ocaml < 4.03 2021-01-26 11:27:50 +01:00
Simon Cruanes
3068aacc84
Merge pull request #349 from c-cube/cceither
feat: add CCEither module
2021-01-25 14:02:07 -05:00
Fardale
23bcc8887c feat: add CCEither module 2021-01-25 19:52:16 +01:00
Fardale
124a808b54 chore(CI): test core on non ubuntu platform 2021-01-25 19:44:00 +01:00
Fardale
a6318949f6 chore(CI): run test only on Ubuntu 2021-01-25 18:51:11 +01:00
Fardale
663416b350 fix: change container.data to container-data in README.md 2021-01-25 18:04:28 +01:00
JPR
383baf4464 Syncing comments 2021-01-25 15:57:03 +01:00
Simon Cruanes
ae886c2f08 add CCList.chunks 2021-01-24 11:18:53 -05:00
Simon Cruanes
e037ca1afa doc: fix a link in the readme
close #347
2021-01-22 11:08:28 -05:00
Simon Cruanes
5f7b03d83b doc: refresh readme 2021-01-12 12:32:45 -05:00
Simon Cruanes
646ea1645c doc: update readme 2021-01-12 11:53:52 -05:00
Simon Cruanes
f9e9c39c37 feat: add iter/seq functions to CCString 2021-01-12 11:53:37 -05:00
Simon Cruanes
fc57765c31 fix(ccint): pick popcount at runtime on 64 bits
(we can compile on 64 bits, at least for bytecode, and execute
on 32 bits native or jsoo's 32 bits; therefore we need to pick the
implementation at runtime).
2020-12-28 17:13:02 -05:00
Simon Cruanes
858dee7279 fix: in shims, use configurator properly to determine int size
close #346
2020-12-27 22:44:20 -05:00
Simon Cruanes
bb8a9d02d7 chore: try to optimize CI a bit further 2020-12-22 10:45:43 -05:00
Simon Cruanes
fb4891dac3 yoloize the github CI cache 2020-12-22 10:45:43 -05:00
Simon Cruanes
12ac1de588 be nicer to mac OS' lazy scheduler in tests 2020-12-22 10:45:43 -05:00
Simon Cruanes
330e026d0a try to enable cache in CI 2020-12-22 10:45:43 -05:00
Kye W. Shi
fd1a43497d
CCImmutArray: add tests (#344)
CCImmutArray: add tests
2020-12-16 23:39:46 -05:00
Kye Shi
78681736cd CCFormat: add @since tags for space/append/etc. 2020-12-14 13:36:35 -05:00
Kye W. Shi
82781aa9c7 CCFormat: List.iter instead of fold_left for append_l (more efficient)
Co-authored-by: Simon Cruanes <simon.cruanes.2007@m4x.org>
2020-12-14 13:36:35 -05:00
Kye Shi
d0b05fdb76 CCFormat: add append, append_l, infix ++ for sequencing 2020-12-14 13:36:35 -05:00
Kye Shi
b1643cfbd5 CCFormat: add space, break, cut aliases 2020-12-14 13:36:35 -05:00
Kye Shi
d5f2c6b861 CCList: add reduce function (resolves #305) 2020-12-13 17:15:43 -05:00
Simon Cruanes
4b68dc204a chore: activate CI on PRs 2020-12-13 17:14:28 -05:00
Darren
c8d0c60657
Init files for fuzzing (#339)
* Init files for fuzzing
* Fixed fuzz/run.sh
* Added fuzzing for CCUtf8_string.uchar_to_bytes
2020-12-09 16:10:02 -05:00
Simon Cruanes
fcd1247ec8 test: add stronger test to compare with uutf in ccutf8string 2020-12-07 23:42:31 -05:00
Simon Cruanes
52abbcd978 fix(sexp): handle non-ascii escapes in strings
close #338
2020-12-07 23:33:34 -05:00
Simon Cruanes
43701e6726 test: add regression test for #338 2020-12-07 23:32:40 -05:00
Simon Cruanes
e16926fa5f add tests for utf8string 2020-12-07 23:32:00 -05:00
Simon Cruanes
3918ed1155 feat(utf8): add and expose uchar_to_bytes
rather than encoding to buffers directly, we can expose an iterator
over the bytes of an uchar.
2020-12-07 23:31:05 -05:00
Simon Cruanes
f9ee8d0e89 prepare for 3.1 2020-12-04 18:39:54 -05:00
Simon Cruanes
f3e808e870 Merge branch 'wip-ci-2020-11-10' 2020-12-04 17:48:03 -05:00
Simon Cruanes
133aed683c fix build 2020-11-13 17:58:18 -05:00
Simon Cruanes
ca7801a854 fix: use shims again for CCList.(and&) 2020-11-13 15:36:55 -05:00
Simon Cruanes
9068cbc1cc fix tests and build 2020-11-10 18:06:30 -05:00
grayswandyr
057427cb72 Apply suggestions from code review
Co-authored-by: Simon Cruanes <simon.cruanes.2007@m4x.org>
2020-11-10 18:04:27 -05:00
David Chemouil
3912b288e8 add List.combine_chop and corresponding (and&) synchronized product 2020-11-10 18:04:27 -05:00
Simon Cruanes
60eee785a9 doc: update readme 2020-11-10 14:06:19 -05:00
Simon Cruanes
af05520e3b chore: try and remove travis to use github CI instead 2020-11-10 14:06:03 -05:00
JPR
b3e32c587f Comments 2020-11-06 16:57:01 -05:00
David Chemouil
61a8cc58bd add a guard function for list comprehensions 2020-11-05 12:10:14 -05:00
Simon Cruanes
26df938968 more doc 2020-11-05 12:05:31 -05:00
Simon Cruanes
7e160106c5 doc: explain a bit more the Traverse submodule of list 2020-11-05 11:54:48 -05:00
Simon Cruanes
87a9937938 doc: indicate that ccmutheap is experimental 2020-11-04 12:29:15 -05:00
Simon Cruanes
587e445308 doc: add missing @since 2020-10-30 13:06:07 -04:00
Simon Cruanes
9643ee94a9 test: add some basic tests to CCMutHeap 2020-10-07 12:03:37 -04:00
Simon Cruanes
aed72685fc refactor: ask for lt in the mutable heap arg 2020-10-07 12:03:27 -04:00
Simon Cruanes
3484efc691 feat: add mutable heap with increase/decrease to containers-data
this code is adapted from msat
2020-10-07 11:45:58 -04:00
Simon Cruanes
9ca278dc51 fix obsolete comment 2020-10-04 12:50:04 -04:00
Simon Cruanes
e0f14837ac fix warning 2020-09-23 21:34:14 -04:00
Simon Cruanes
264c9b608e un-specify order of elements in CCMap.to_list 2020-09-21 13:49:50 -04:00
Simon Cruanes
5ee25afad5 test: add a test for update 2020-09-21 13:49:49 -04:00
Josh Berdine
e6f77edf1a Move definition of CCMap.update so that it is shadowed by Stdlib.Map.update
Signed-off-by: Josh Berdine <josh@berdine.net>
2020-09-21 13:49:25 -04:00
Josh Berdine
d81cba4b06 Update contributing instructions in README
Signed-off-by: Josh Berdine <josh@berdine.net>
2020-09-21 09:47:58 -04:00
Simon Cruanes
652c823978 fix(intmap): order of arguments for the HO param should be stable
close #329
2020-09-08 10:42:10 -04:00
Simon Cruanes
9a9ae12972 chore: try to fix travis 2020-08-24 10:35:40 -04:00
Simon Cruanes
a59562bed1 prepare for 3.0.1 2020-08-24 10:13:03 -04:00
Simon Cruanes
50ec164b67 fix: remove code that is in the shims 2020-08-06 11:19:52 -04:00
Simon Cruanes
09298b3324 small change in shims generation
as @fardalem points out, better be conservative on archictures and have
64bits popcount be the special case
2020-08-06 09:46:33 -04:00
Simon Cruanes
e0f2c78edd fix(int): use shims to provide separate 32/64 bits versions of popcount
close #327
2020-08-05 14:05:48 -04:00
Simon Cruanes
9fe414f793 chore: small refactor of github actions
sadly the matrix thing is not expressive enough to use a
given switch only on a given OS (like, windows + mingw32).
So, 🤷
2020-08-05 14:05:12 -04:00
Simon Cruanes
38d8fc2f9a doc: update readme 2020-08-04 09:42:55 -04:00
Simon Cruanes
ade2500e68 prepare for 3.0, without further ado 2020-07-31 15:51:46 -04:00
Simon Cruanes
089a1bec16 try to fix tests that fail on mac OS 2020-07-31 15:29:49 -04:00
Simon Cruanes
8a60d44946 ci: try to test on mac OS again, amend tests, make mdx optional 2020-07-30 19:12:49 -04:00
Simon Cruanes
d60bea1a98 fix build for 4.11 by working around -nolabels 2020-07-30 10:02:16 -04:00
Simon Cruanes
8683153cc9 fix opam files 2020-07-28 20:19:58 -04:00
Simon Cruanes
01d9693a16 wip: fix travis 2020-07-28 20:17:33 -04:00
Simon Cruanes
add6a58cf5 prepare for 3.0~rc1 2020-07-28 18:15:05 -04:00
Simon Cruanes
2eb58dd6c4 prepare readme and changelog for 3.0~rc1 2020-07-28 18:12:12 -04:00
Simon Cruanes
5da10f49a2 doc: small fix 2020-07-28 17:52:55 -04:00
Simon Cruanes
211cd5863b feat: add infix operators to String
close #315
2020-07-28 17:34:59 -04:00
Simon Cruanes
39e0ad2395 fix(pool): missing emptiness check in Fut.map_l
also add regression test
2020-07-28 16:27:23 -04:00
Simon Cruanes
30b9307a70
Merge pull request #325 from c-cube/ccpair_map
break(CCPair): use more standard name for some map functions
2020-07-27 22:58:58 -04:00
Fardale
01da25cead break: change pp functions to take unit printer for sep/stop/start
sep/stop/start -> pp_sep/pp_stop/pp_start
string -> unit printer
2020-07-27 22:57:29 -04:00
Fardale
3b2030f6f2 break(CCPair): use more standard name for some map functions
map1 -> map_fst
map2 -> map_snd
map_fst -> fst_map
map_snd -> snd_map
introduce map2 and map_same2
Fix #316
2020-07-27 21:39:17 +02:00
Simon Cruanes
4ac67a7518
chore(ci): fix bad test logic 2020-07-24 09:58:17 -04:00
Simon Cruanes
1f1b859ec7 Merge remote-tracking branch 'origin/wip-github-ci' 2020-07-23 12:56:00 -04:00
Fardale
a8bcbb0e3d chore(CCHeap): drop old comment 2020-07-23 16:32:52 +02:00
Fardale
b6b2c68913 break(CCGraph): remove deprecated module and function
Remove Seq and pp_seq
2020-07-23 16:32:52 +02:00
Simon Cruanes
644e3487a3 test: fix a test that wouldn't pass on 4.03 2020-07-23 16:32:52 +02:00
Simon Cruanes
4122ffa6ab doc: fix some ocamldoc warnings 2020-07-23 16:32:52 +02:00
Fardale
c22fed18de break: convert sequence to iter in data 2020-07-23 16:32:52 +02:00
Fardale
08d59ea07a break: remove klist in data 2020-07-23 16:32:52 +02:00
Fardale
c85c135157 break: remove klist type and functions from core 2020-07-23 16:32:52 +02:00
Fardale
8c3d716ab1 break: rename fonction from *std_seq* to *seq* 2020-07-23 16:32:52 +02:00
Simon Cruanes
fa1f6170d1 chore: github actions: only run tests on linux 2020-07-22 18:24:12 -04:00
Simon Cruanes
aba959aa89 chore(ci): widen support for github CI 2020-07-22 18:00:54 -04:00
Simon Cruanes
c2cab292b1 chore: add github ci 2020-07-22 17:40:59 -04:00
Simon Cruanes
8b41a2bf69 doc: add missing @since 2020-07-22 17:08:48 -04:00
Kye W. Shi
3bae829558 CCResult: add <$> operator 2020-07-22 16:23:04 -04:00
JPR
0a3b04855a ... tweaks 2020-07-02 09:38:25 -05:00
JPR
063a59eee4 Comments 2020-07-02 09:38:25 -05:00
Fardale
8b319edbe1
doc(core): add link to the doc of std module (#320) 2020-06-30 17:31:52 +02:00
Fardale
20234cdd22 fix(CCVector): rename shrink into truncate 2020-06-30 09:02:52 -05:00
Simon Cruanes
1469c72f30 doc: remove list of authors and point to github instead
this prevents the list of authors from getting stale or outdated,
by just relying on existing tools
2020-06-19 21:22:08 -04:00
Kye W. Shi
06b795c604 CCFun: add infix tests 2020-06-19 20:17:30 -05:00
Kye W. Shi
6cfa7307de CCFun: include module type of Infix 2020-06-19 20:17:30 -05:00
Kye W. Shi
a03b6e68e3 CCFun: put infix operators in Infix module 2020-06-19 20:17:30 -05:00
Fourchaux
a5da43511b
comments modifs (#311) 2020-06-17 09:21:31 +02:00
Fourchaux
53febce5a9
Comments presentation (#310)
* Comments presentation
2020-06-12 09:52:50 +02:00
Simon Cruanes
54099f10d5 test: regression test for stack overflow in CCpool 2020-05-24 19:12:04 -04:00
Fardale
685efeae28
Merge pull request #308 from FardaleM/ccint
CCInt{,32,64} and CCNativeint
2020-05-24 00:53:08 +02:00
Fardale
364a9ba1cb
Merge pull request #309 from FardaleM/clean_ccarray
chore(array): clean CCArray and CCArrayLabels
2020-05-24 00:52:49 +02:00
Fardale
5dc96ebfa8 chore(array): clean CCArray and CCArrayLabels
Remove function define in the stdlib and in Infix
2020-05-23 12:52:26 +02:00
Fardale
8c0d11546e small doc fix 2020-05-23 11:37:56 +02:00
Fardale
b8ca053a48 feat(ccnativeint): complete CCNativeint with regards to CCInt 2020-05-23 11:37:56 +02:00
Fardale
f2c0bc7d09 chore(ccnativeint): clean CCNativeint.mli
Remove duplicate functions from Infix and Nativeint
2020-05-23 11:37:56 +02:00
Fardale
1fec2f0f96 chore(ccint64): clean CCInt64.mli
Remove duplicate functions from Infix and Int64
2020-05-23 11:37:56 +02:00
Fardale
efc162125a chore(int32): clean CCInt32.mli
Remove duplicate function from Int32 and Infix module
2020-05-23 11:37:56 +02:00
Fardale
576ee2ede8 feat(Int64): complete CCInt64 with regards to CCInt 2020-05-23 11:37:56 +02:00
Fardale
b7dcc7ed2a chore(int32): change order of definition 2020-05-23 11:37:56 +02:00
Fardale
92b31bedb2 chore(int64): remove duplicate functions between Int64 and CCInt64 2020-05-23 11:37:56 +02:00
Fardale
e574309763 feat(CCInt32): complete CCInt32 with regards to CCInt 2020-05-23 11:37:56 +02:00
Fardale
102ad62075 fix: correct module name in Invalid_argument for CCInt.range_by 2020-05-23 11:37:56 +02:00
Fardale
b06155f05b implement CCInt.sign using CCInt.compare and add more doc 2020-05-23 11:37:56 +02:00
Fardale
eaa421c62d chore(int): include module Int for ocaml >= 4.08 2020-05-23 11:37:56 +02:00
Fardale
8f5c7c8fe9 feat(int): add elements of Int module from the std 2020-05-23 11:37:56 +02:00
Simon Cruanes
5dd90edafa add CCResult.get_lazy 2020-05-21 15:26:27 -04:00
Simon Cruanes
d7a7cbb170 feat: add Int.popcount operator 2020-05-18 14:04:42 -04:00
Simon Cruanes
4b09adaa5a fix typo 2020-05-15 15:28:27 -04:00
Raphael Sousa Santos
4936cb60d4 Add CCFloat.pi 2020-05-15 14:27:59 -05:00
Simon Cruanes
18222af1a3 perf: a few more inline annotations 2020-05-12 12:46:12 -04:00
Simon Cruanes
97e49b6a5c fix: expose always_eq/never_eq in CCEqual
see #232
2020-05-06 10:33:28 -04:00
Simon Cruanes
b26021a976 feat(string): add optional cutoff arg on String.edit_distance
also add more tests
2020-04-30 22:19:49 -04:00
Simon Cruanes
d99d35cc70 chore: travis detail 2020-04-30 22:19:28 -04:00
Simon Cruanes
2153c8a5e6 fix(dune): only build containers-data.top in bytecode 2020-04-30 22:19:06 -04:00
Simon Cruanes
47b631d3f4
Merge pull request #304 from FardaleM/mem
harmonize CCList.mem and CCArray.mem
2020-04-28 16:41:50 -05:00
Fardale
7dd45a0fa8 fix(CCArray) fix use of Stdlib for ocaml < 4.07 2020-04-28 22:31:33 +02:00
Fardale
789eee9d53 CCArray: add @since 2020-04-28 22:26:42 +02:00
Fardale
4267210da9 CCArray: use raise_notrace instead of raise 2020-04-28 22:26:20 +02:00
Fardale
2faa3fbae5 chore(CCVector): rename remove to remove_and_shift 2020-04-28 08:53:35 -05:00
Fardale
c8d61a1248 feat(CCVector): add remove
remove keep the order of the element but need to move
all element after the removed one.
2020-04-28 08:53:35 -05:00
Fardale
c1461940c2 wip(3.0): rename CCVector.remove to CCVector.remove_unordered 2020-04-28 08:53:35 -05:00
Fardale
c50672ff7a feat(CCArray): add optional argument eq to mem 2020-04-28 15:41:01 +02:00
Fardale
2025a62536 feat(CCList): make mem compatible with the Stdlib
The eq argument is now optional.
2020-04-28 15:39:47 +02:00
JPR
0f9e51fbe3 Comments presentation 2020-04-26 16:17:17 -05:00
Simon Cruanes
0fea0ea522 opam and travis 2020-04-24 22:47:37 -04:00
Simon Cruanes
5d536afb68 try to fix test 2020-04-24 22:24:05 -04:00
Simon Cruanes
d7bf01f118 chore: update travis 2020-04-24 21:43:35 -04:00
Simon Cruanes
df5151636b improvements 2020-04-24 21:11:44 -04:00
Simon Cruanes
d923795e1a remove slice APIs in string and array 2020-04-24 20:23:26 -04:00
Simon Cruanes
a767e4618d wip(3.0): remove deprecated functions, in particular sequence 2020-04-24 20:16:53 -04:00
Simon Cruanes
46e40c9165 delete containers.iter and merge parts of it into containers-data 2020-04-24 19:48:42 -04:00
Simon Cruanes
a2d07e4028 chore: update tests to accomodate for split into several libs 2020-04-24 19:27:06 -04:00
Simon Cruanes
b1b5d31665 breaking: move CCSexp into the core library, delete containers.sexp 2020-04-24 19:13:54 -04:00
Simon Cruanes
49545decbf update top packages 2020-04-24 19:07:56 -04:00
Simon Cruanes
da46adb370 chore: split into distinct packages for containers-data and containers-thread 2020-04-24 18:59:39 -04:00
Simon Cruanes
e43f658fa9
Merge pull request #299 from copy/master
add more benchmarks
2020-04-19 17:28:14 -05:00
Fabian
b82d31adf1 Improve use of opaque_identity 2020-04-19 18:05:14 +02:00
Fabian
dafae58e15 Extend benchmark: to_array, cons and cons_fold 2020-04-19 17:59:14 +02:00
Fabian
d681a34caa Extend benchmark: Sek, iter and pop 2020-04-19 17:58:39 +02:00
Fabian
a1233392f0 fix naming 2020-04-19 17:46:00 +02:00
Fabian
d72907302a Disable core_kernel test for now 2020-04-19 17:42:13 +02:00
Fabian
53d5a80b96 benchmark for memory usage of data structures 2020-04-19 17:37:25 +02:00
Simon Cruanes
952b664a68 test for funvec 2020-04-18 22:14:35 -04:00
Simon Cruanes
15fb26249f fix(funvec): error in pop
close #298
2020-04-18 22:10:35 -04:00
Simon Cruanes
d34b7588b0 test: another test 2020-03-29 21:50:39 -04:00
juloo
203723d350 CCSexp: Escape empty atoms
Otherwise (`List [ `Atom "" ]) would be formatted as "()", which is (`List []).
2020-03-29 20:48:56 -05:00
JPR
a5b8a0aa18 Substitute 'Pervasives' with 'Stdlib' 2020-03-24 10:43:49 -04:00
JPR
76c1c98bbf Modifs comments 2020-03-16 15:56:12 -05:00
Simon Cruanes
fb6483539e feat(fmt): add exn combinator 2020-03-07 11:26:00 -06:00
Simon Cruanes
d12213da31 doc: missing raise annotation 2020-03-07 11:25:49 -06:00
Simon Cruanes
dc10a55a75 fix typo
close #289
2020-03-06 08:37:02 -06:00
Fardale
93be8b2cd8 fix *Labels.mli for ocaml <= 4.04 2020-03-05 17:50:30 -06:00
Fardale
c59147f1d1 clean(String): remove functions defined in Stdlib 2020-03-05 17:50:30 -06:00
Fardale
baec1f466e clean(Set): remove functions defined in Stdlib 2020-03-05 17:50:30 -06:00
Fardale
71a3ebdeb5 clean(Nativeint): remove functions defined in Stdlib 2020-03-05 17:50:30 -06:00
Fardale
80ad2f349f clean(Map): remove functions defined in Stdlib 2020-03-05 17:50:30 -06:00
Fardale
0d9b4d910a clean(List): remove functions defined in Stdlib 2020-03-05 17:50:30 -06:00
Fardale
4f0e219036 clean(Int64): remove functions defined in Stdlib 2020-03-05 17:50:30 -06:00
Fardale
7821e8e259 clean(Int32): remove functions defined in Stdlib 2020-03-05 17:50:30 -06:00
Fardale
b96f7d6e68 clean(Char): remove functions defined in Stdlib 2020-03-05 17:50:30 -06:00
Fardale
f3719d29aa clean(Array): remove functions defined in Stdlib 2020-03-05 17:50:30 -06:00
Fardale
2bde1e4dd3 doc(CCInt): add doc for of_float 2020-03-05 15:42:24 +01:00
Simon Cruanes
cd26e3d3a3 IO: add copy_into for transferring data between channels 2020-01-28 13:03:08 -06:00
Fardale
618e57fdd7 examples: remove lambda.ml 2020-01-27 13:55:50 -06:00
Fardale
51a532ce59 examples: clean up 2020-01-27 13:55:50 -06:00
Fardale
8c43e345ad doc(CCFloat): document the rounding behavior
fixes #237
2020-01-24 23:01:47 +01:00
Fardale
d1a5e047fe feat(CCResult): add get_lazy
fixes #285
2020-01-24 22:57:25 +01:00
Fardale
9d083df3a6 feat(CCInt): add of_float 2020-01-18 16:16:52 +01:00
Fardale
2d1ba8d925 feat(CCInt): add of_string_exn 2020-01-18 15:57:55 +01:00
Simon Cruanes
c1b976d0d3 Merge branch 'br-2.8.1' 2020-01-13 08:30:19 -06:00
Fabian
50042d6c09 Document raising behaviour of CCIO.tee 2020-01-12 20:30:18 -06:00
Fabian
2bf052ab7a Avoid catch-all handlers 2020-01-12 20:30:18 -06:00
Simon Cruanes
8c33147d06 add some tests 2020-01-12 20:28:22 -06:00
Simon Cruanes
527c4414d9 prepare for 2.8.1 2020-01-12 18:18:33 -06:00
Simon Cruanes
2e3393f81e fix: add missing CCVector.of_iter 2020-01-12 18:18:33 -06:00
Fardale
2b5f2fce11 feat(CCVector): add filter_in_place, deprecate filter' 2020-01-08 23:17:53 +01:00
Fardale
4bc01a0b82 feat(opt): fix bind arguments order 2020-01-06 21:39:55 +01:00
Fardale
bf8db5dcff feat(opt): add bind 2020-01-06 16:15:52 +01:00
Fardale
1dcc529623 feat(float): add of_string_opt 2020-01-06 16:15:05 +01:00
Simon Cruanes
5143e28ce8 doc: fix readme url 2019-12-17 08:05:17 -06:00
Simon Cruanes
5126973173 prepare for 2.8 2019-12-14 17:50:35 -06:00
Simon Cruanes
ab494fb753 fix: compat issue 2019-12-14 17:50:35 -06:00
Simon Cruanes
7d9d9d45b8 feat(list): add indexed functions and fold_on_map
close #222
2019-12-14 16:52:35 -06:00
Simon Cruanes
1947d1804b refactor: also port CCGraph to iter 2019-12-14 16:41:49 -06:00
Simon Cruanes
264e89b198 chore: makefile 2019-12-14 16:32:24 -06:00
Simon Cruanes
138047ef11 feat: add {to,of,add}_{iter,std_seq} where relevant; deprecations
deprecate `seq` named functions (for `iter`)
deprecate klist functions (for `std_seq`)

close #231
2019-12-14 16:29:07 -06:00
Simon Cruanes
a836b7ed8b feat(unix): add ensure_session_leader and add some docs 2019-12-14 14:48:46 -06:00
Simon Cruanes
52ef092a4c feat(pool): add infix operators on futures 2019-12-14 14:37:30 -06:00
Simon Cruanes
faeae964fc fix(pp): improve printing of hashtables
close #243
2019-12-14 14:25:38 -06:00
Simon Cruanes
bcb90de435 doc: small change to readme 2019-12-14 14:22:35 -06:00
Simon Cruanes
c92a09ac9d add more deprecation comments 2019-12-14 14:04:09 -06:00
Simon Cruanes
af4662e962 deprecate CCVector.fill_empty_slots_with 2019-12-14 13:57:56 -06:00
Fardale
0b759c67b8 CCVector: update the doc 2019-12-14 13:44:57 -06:00
Fardale
27eb874523 CCVector: resize_ take the elt as argument
It is safer to have the check on the size of the underlying array
close to access of the first element.
2019-12-14 13:44:57 -06:00
Simon Cruanes
b69cc33c26 perf: make let-op functors inline, hopefully 2019-12-14 12:08:48 -06:00
Simon Cruanes
9b76f277f7 more fixes for the shims 2019-12-14 12:08:48 -06:00
Simon Cruanes
616c077880 fix: proper shims for CCArrayLabel too 2019-12-14 12:08:48 -06:00
Simon Cruanes
d5e5b43e1f fix shims for arrays 2019-12-14 12:08:48 -06:00
Simon Cruanes
74e7d87872 fix: backport a pseudo Floatarray pre 4.06 2019-12-14 12:08:48 -06:00
Simon Cruanes
62672fdbd2 fix test 2019-12-14 12:08:48 -06:00
Simon Cruanes
9a672f4e3c chore(travis): disable build on 4.03, enable tests on 4.08/09 2019-12-14 12:08:48 -06:00
Simon Cruanes
b2d9e69042 feat: put the let operators inside the Infix modules when relevant 2019-12-14 12:08:48 -06:00
Simon Cruanes
bf0227d404 feat: on 4.08, support let operators
close #276
2019-12-14 12:08:48 -06:00
Simon Cruanes
037a0ef922 Merge branch 'wip-ccvec' 2019-12-14 12:08:33 -06:00
Fardale
bbdcd93417 wip: improved gc behavior for ccvector 2019-12-14 11:50:47 -06:00
Fardale
1d589cf4ac use array_is_empty_ instead of direct check 2019-12-14 11:50:47 -06:00
Simon Cruanes
5cb715a206 fix: properly alias to CCChar in containers.ml
close #281
2019-12-14 11:42:19 -06:00
Simon Cruanes
70bd7f1e97 fix: move with_temp_dir from CCIO to CCUnix
it depends on unix, which is a deal breaker for core
2019-12-11 09:46:21 -06:00
Simon Cruanes
28f8872ef5 feat: add monoid_product to Array and Vector 2019-12-10 21:38:07 -06:00
Simon Cruanes
5bcb8c63ad fix: missing result 2019-12-10 21:36:35 -06:00
Simon Cruanes
1b5b23a8f1 remove unlabel, remove all traces of Result 2019-12-10 20:48:38 -06:00
Simon Cruanes
30251e9426 bump minimum version of OCaml to 4.03, drop deps {result,uchar}
close #274
2019-12-10 19:56:54 -06:00
Simon Cruanes
858616606b add CCVector.shrink_to_fit to limit memory usage 2019-12-10 16:39:35 -06:00
Simon Cruanes
1239960c42 wip: improved gc behavior for ccvector 2019-12-10 16:39:35 -06:00
Simon Cruanes
236a0c43ce feat: add CCVector.clear_and_reset 2019-12-10 16:39:35 -06:00
Simon Cruanes
0ef454c6dc feat(sexp): expose parse_string_list and the list decoder 2019-12-10 15:50:18 -06:00
Simon Cruanes
08f333ffa8 chore: opam file 2019-12-10 15:50:04 -06:00
Simon Cruanes
bf2dc512fd
Re-enable mdx tests (#277)
ee-enable mdx tests
2019-12-10 15:48:58 -06:00
Simon Cruanes
480a7f85fc
older dune dialect 2019-12-10 15:22:08 -06:00
Simon Cruanes
b3bfa82ccb
use older dune dialect 2019-12-10 15:21:51 -06:00
Simon Cruanes
c2f8d6811b fix(sexp): set location properly when parsing a file 2019-12-06 20:21:46 -06:00
Simon Cruanes
825e350da0 feat(sexp): reexport atom/list cstors 2019-12-05 15:19:38 -06:00
Nathan Rebours
58a17202d7 Re-enable mdx tests 2019-12-04 15:47:39 +01:00
Simon Cruanes
76bec991a2 feat(io): add with_temp_dir function 2019-12-03 12:49:22 -06:00
Simon Cruanes
8ddc3de490 chore: require dune configurator 2019-12-03 08:24:49 -06:00
Simon Cruanes
936d5912e6 chore: fix benchs so they don't depend on clarity and they compile again 2019-11-20 20:59:45 -06:00
Simon Cruanes
7d1862a501 wip: add to_std_seq
see #254
2019-11-20 17:50:40 -06:00
Simon Cruanes
78ef007b77 deprecate CCOpt.to_seq, provide to_iter instead 2019-11-20 17:50:22 -06:00
Simon Cruanes
bd4e4d311d add CCOpt.value to improve compat with Stdlib.Option 2019-11-20 17:49:49 -06:00
Fardale
3804dbff86 add CCVector.mapi 2019-11-17 10:43:18 -06:00
Simon Cruanes
968a39b6bc fix: restore CCSexp.atom which was lost in 2.7 2019-11-13 15:30:11 -06:00
Simon Cruanes
7bdf6f6cef test: use more deterministic test for timer 2019-11-12 10:17:50 -06:00
Simon Cruanes
7cefde490b prepare for 2.7
remove mdx
2019-11-12 08:49:33 -06:00
Simon Cruanes
2241a25d9e CCIO: use attributes for warnings 2019-11-11 20:01:21 -06:00
Simon Cruanes
e106432e21 deprecate CCKList in favor of the standard Seq
see #257
2019-11-11 19:59:37 -06:00
Simon Cruanes
27fb393698 feat(CCIO): add _gen suffixes to some functions
deprecate the previous versions
see #261
2019-11-11 19:55:43 -06:00
Simon Cruanes
edcd97a7ed doc(IO): explain lifetime of gen a bit 2019-11-08 11:27:01 -06:00
Simon Cruanes
11d081a612 doc: add pointer to the repo for gen 2019-11-08 11:17:37 -06:00
Simon Cruanes
09c205db78 fix: missing interface file 2019-11-05 20:16:52 -06:00
Simon Cruanes
853c9f27bd refactor: reuse a bit of code 2019-11-05 19:43:37 -06:00
Simon Cruanes
404e35f850 feat(sexp): provide ability to annotate parsed S-exprs with their position 2019-11-05 19:41:44 -06:00
Simon Cruanes
d6f98032c8 feat(sexp): functorize the parser/printer 2019-11-05 19:24:28 -06:00
Simon Cruanes
2b6d9126c1 feat(ccsexp): support #; for commenting a sexp 2019-11-05 19:02:40 -06:00
Simon Cruanes
2ed821bbe1 style: reindent in ccdeque 2019-11-05 18:23:06 -06:00
Fardale
2fa12665dd change type of CCDeque
Change the definition of the type in CCDeque to remove the Zero cell.
This new type enforce one invariant.
2019-11-04 21:47:09 -06:00
Simon Cruanes
b3ce398624 fix: remove dep from vec to list 2019-10-30 14:28:17 -05:00
Fardale
0dafceb708 Adding to_string (#270)
* add `CCArray.to_string`
* add `CCArrayLabels.to_string`
* add `CCList.to_string`
* add `CCListLabels.to_string`
* add `CCChar.to_string`
* add `CCPair.to_string`
* add `CCHeap.to_string`
* add `CCSet.to_string`
* add `CCVector.to_string`
2019-10-30 14:26:52 -05:00
Simon Cruanes
c1704d71ff style: improve new code 2019-10-29 21:50:22 -05:00
Fardale
509dacb96f add CCDeque.{remove_*;update_*}
functions added:
CCDeque.remove_back
CCDeque.remove_front
CCDeque.update_back
CCDeque.update_front
2019-10-26 17:28:51 -05:00
Fardale
a33963c335 add CCDeque.{*_opt}
Functions added:
CCDeque.peek_front_opt
CCDeque.peek_back_opt
CCDeque.take_back_opt
CCDeque.take_front_opt
2019-10-23 19:16:07 -05:00
Fardale
035aac9a72 add invariant in CCDeque.ml 2019-10-23 19:16:07 -05:00
Simon Cruanes
1b8d9ca9a6 style: small fix 2019-10-22 18:14:59 -05:00
Simon Cruanes
c4631b78dc add CCDeque.{filter,filter_map} 2019-10-22 18:14:59 -05:00
Simon Cruanes
546cbd85fd add CCDeque.filter_in_place 2019-10-22 18:14:59 -05:00
Simon Cruanes
70d7dd234d add CCBool.{to,of}_int 2019-10-21 10:34:26 -05:00
Simon Cruanes
df9bbb8746 add Result.flatten_l to turn a list of results into a result of list 2019-10-16 12:22:55 -05:00
Simon Cruanes
0c23e3ba88
Merge pull request #266 from c-cube/wip-264-improve
improve on #264
2019-10-11 17:44:42 -05:00
Simon Cruanes
b25cf1ea00 fix: forgot const 2019-10-11 17:05:34 -05:00
Simon Cruanes
ab5b3aa6af refactor: remove stdlib's code, simple reimplementation of Stdlib.Fun
keep the whole codebase BSD.
2019-10-11 16:59:22 -05:00
Simon Cruanes
0867583209 test: fix qtest generation to ignore the stdlib's fun module 2019-10-11 16:45:39 -05:00
Simon Cruanes
d63bdbc0eb fix syntax error in ArrayLabels.mli 2019-10-11 16:29:50 -05:00
Simon Cruanes
6d02200429 fix syntax error in Array.mli 2019-10-11 16:29:50 -05:00
narimiran
3658864a5a add CCArray.Infix 2019-10-11 16:29:50 -05:00
Christopher Zimmermann
6a51830305 Don't use raise_with_backtrace in backward-compatible Fun.protect 2019-10-07 14:07:11 +02:00
Christopher Zimmermann
f759a92214 travis: test on OCaml 4.09, too. 2019-10-07 14:01:20 +02:00
Christopher Zimmermann
5fcd1a506e add shim for backward-compatibility Fun module 2019-10-07 13:42:48 +02:00
Christopher Zimmermann
858af75ee8 Make use new Stdlib.Fun to extend / replace CCFun 2019-10-07 12:28:22 +02:00
Christopher Zimmermann
2d5f0e3e8d Document behaviour of Fun.finally when finaliser raises 2019-10-07 12:26:21 +02:00
Simon Cruanes
beb38da150 prepare for 2.6.1 2019-09-06 20:14:42 -05:00
Simon Cruanes
080f81a9dd fix(parse): error in many 2019-09-06 14:12:08 -05:00
Simon Cruanes
19fbd33278 chore: add 4.08 to travis 2019-08-09 16:16:37 -05:00
Kate
e7c265bcf9 Fix Containers.Stdlib on OCaml 4.07 2019-07-05 11:08:55 -05:00
Simon Cruanes
a1f3f4781d prepare for 2.6 2019-06-19 10:02:55 -05:00
Simon Cruanes
21feaaf1ca fix compilation on < 4.08 2019-06-19 10:02:55 -05:00
Simon Cruanes
7ff5aa0d18 fix: add shims for CCArray 2019-06-18 23:10:26 -05:00
Simon Cruanes
f71472ad08 fix: remove reference to sequence 2019-06-14 18:26:15 -05:00
Simon Cruanes
0c00cafb20 use markdown again for changelog 2019-06-14 18:26:14 -05:00
Simon Cruanes
96ed8a37ab feat: introduce shim modules for 4.08 compat
- also make `unlabel` an explicit operation
- use `Stdlib` instead of `Pervasives`
- remove some warnings in Format
2019-06-14 18:26:14 -05:00
Simon Cruanes
fa0290061b doc: fix bad example in CCIO 2019-05-31 09:27:42 -05:00
JPR
8e3dc5e006 Small typos 2019-05-14 11:14:12 -05:00
rymdhund
05e49a27e7 Remove unused reference 2019-04-29 09:58:29 -05:00
Simon Cruanes
9bdf31b07c fix: missing whitespace in readme 2019-04-13 05:49:31 -05:00
Simon Cruanes
b07b030898 doc: update readme a bit 2019-04-13 03:25:26 -05:00
Simon Cruanes
3712db3a5b chore: use iter, not sequence, in tests 2019-04-13 03:20:56 -05:00
Simon Cruanes
0d6c922eb1 fix: use same evaluation order as stdlib for CCList.init
close #256
2019-04-13 03:10:38 -05:00
Simon Cruanes
1654f8c826 test: add regression test for #256 2019-04-13 03:10:29 -05:00
Simon Cruanes
a325600ccb chore: modernize make watch 2019-04-13 03:05:34 -05:00
Christopher Zimmermann
bb4d8a89f3 don't use Compenv.module_of_filename
this simple implementation is good enough for us.
2019-03-07 23:15:24 +00:00
nilsbecker
c70825b250 ocamldebug section in the README (#249)
add `ocamldebug` instruction section to the README
2019-02-27 14:48:56 +00:00
Simon Cruanes
d9555ae063 chore: remove appveyor, too unreliable 2019-02-19 18:48:15 -06:00
Simon Cruanes
bfa5d9adde breaking: make Array.random_choose raise invalid_arg instead of not_found 2019-02-19 18:46:08 -06:00
Simon Cruanes
f190964cfd fix: make Array.random_choose fail on empty array at creation time
see #246
2019-02-18 20:41:17 -06:00
Simon Cruanes
c893716c1a feat: add remove function to het map/tbl 2019-02-16 16:15:24 -06:00
Simon Cruanes
3236d3c8b9 fix: missing type annotation for specializing int.compare 2019-02-04 13:33:52 -06:00
Simon Cruanes
f7327197fe chore: fix travis 2019-02-02 22:01:45 -06:00
Simon Cruanes
6f681256f7 fix readme across versions 2019-02-02 21:37:11 -06:00
Simon Cruanes
18024cc5e9 doc: hide some parts of the readme 2019-02-02 20:35:50 -06:00
Simon Cruanes
b308680bee doc: update doc/containers, migrate it to mdx 2019-02-02 20:34:21 -06:00
Simon Cruanes
404d74ac43 chore: remove ocamlinit file 2019-02-02 20:31:13 -06:00
Simon Cruanes
e481777c43 doc: migrate readme to .md, using mdx to test it 2019-02-02 20:27:15 -06:00
Simon Cruanes
d0a0ecb3ef doc: remove link to gitter… 2019-02-02 14:15:53 -06:00
Simon Cruanes
c10ad46fbd prepare for 2.5 2019-02-02 14:03:22 -06:00
Simon Cruanes
83251c9efa Merge branch 'br-2.4.1' 2019-02-02 14:01:08 -06:00
Simon Cruanes
8038528097 prepare for 2.4.1 2019-02-02 14:00:02 -06:00
Simon Cruanes
b60fe99365 perf: annotate types in monomorphic/float/int to help specialize builtins
close #245
2019-01-30 21:33:39 -06:00
Calascibetta Romain
052e607c5c CCFQueue.t must be covariant 2019-01-21 17:53:26 +00:00
Calascibetta Romain
23f759b984 Use GADT to discard impossible case on CCFQueue. 2019-01-21 17:53:26 +00:00
Simon Cruanes
dcf66ce502 chore: update travis to build faster on most switches 2019-01-21 11:45:37 -06:00
Simon Cruanes
0d05643c57 chore: try to fix appveyor build 2018-12-23 21:04:50 -06:00
Simon Cruanes
d0f0bf7024 doc: small fix 2018-12-21 17:47:35 -06:00
Simon Cruanes
91adde9743 minor fix 2018-12-21 09:44:28 -06:00
Fardale
23d7ea20f6 [WIP] Work on documentation (#242)
`CCResult` add doc for map_l
2018-12-21 15:43:36 +00:00
Simon Cruanes
f8d9e33900 fix(funvec): expose pop, fix off by one error
close #241
2018-12-11 22:57:06 -06:00
Simon Cruanes
6a90cb25e7 wip: fix retrocompat issues in cclist 2018-12-03 19:59:41 -06:00
Simon Cruanes
ef8d19ac65 some typos in unlabel 2018-12-03 09:47:35 -06:00
Simon Cruanes
197b4e7f1b prepare for 2.4 2018-11-30 10:40:13 -06:00
Simon Cruanes
3b0ceb7821 fix(vector): free elements in more functions, add fill_empty_slots_with
see #235
2018-11-30 10:29:08 -06:00
Fabian
ff58dc0b5f Fix #235 for CCRingBuffer
* Make dummy available to MakeFromArray

* Overwrite deleted elements with a dummy element to allow them to be GCed

* Test that deleted elements can be GCed
2018-11-30 10:14:09 -06:00
Simon Cruanes
b21ca4e0d8 wip: fix(vec): release elements after their removal
see #235
2018-11-30 10:14:09 -06:00
Simon Cruanes
4c5010d381 doc: unleash the Emoji 2018-11-19 11:18:58 -06:00
Nathan Rebours
29a75daac1 Add CCResult.iter_err
Closes #238
2018-11-19 17:17:26 +00:00
Simon Cruanes
da2c9e7c7c doc: abide by odoc's whims 2018-11-02 20:25:37 -05:00
Simon Cruanes
113d03225f add CCEqual.{always,never}_eq
close #232
2018-11-02 20:17:36 -05:00
Simon Cruanes
26c8eb33bf fix: remove spurious Labels module 2018-11-02 20:14:50 -05:00
Christopher Zimmermann
333fa8067e add containersLabels.ml 2018-11-03 01:14:12 +00:00
Simon Cruanes
63f702b21c chore: add synopsis to opam file 2018-11-02 11:54:08 -05:00
Simon Cruanes
f9c59a90e2 doc: remove dead link from readme 2018-11-02 11:35:30 -05:00
Simon Cruanes
6d1f0b9957 rename Random.sample_without_{replacement,duplicates}
- deprecate sample_without_replacement
- use `~cmp` as the named argument, more uniform

close #130
2018-10-16 11:09:27 -05:00
Simon Cruanes
f692fe5dd9 chore: update travis so it's faster 2018-10-13 19:28:04 -05:00
Simon Cruanes
a3222c0908 chore: update opam file to opam2 2018-10-13 19:09:36 -05:00
Simon Cruanes
1d3ba3a6f1 doc: fix small inaccuracy in comments and API 2018-10-13 19:09:36 -05:00
Simon Cruanes
b7e8dcb5ff chore: better deps for unlabel 2018-10-13 19:09:36 -05:00
Christopher Zimmermann
f02f291c7a Create CCEqualLabels 2018-10-13 19:09:36 -05:00
Christopher Zimmermann
c6a3fe86eb Create CCArray_sliceLabels 2018-10-13 19:09:35 -05:00
Christopher Zimmermann
7822f3a045 Create CCStringLabels
CAVE: there is a breaking interface change for String.is_sub
2018-10-13 19:09:35 -05:00
Christopher Zimmermann
f6e1d81ed7 Generate unlabelled interfaces from labelled ones
* add unlabel.ml which parses interfaces and removes labels in function declarations and docstrings. This is a quick hack, but it does the job.
 * an attribute [@keep_label] can be added to the labelled arguments to mark labels like ~cmp which should stay in the "unlabelled" interface
 * While augmenting the labelled interfaces I stumbled across some inconsistencies between the currend labelled / non-labelled interfaces. I used the labelling that made most sense to me and added a "FIXME" comment.
 * Maybe we should break backwards compatibility at some point and name the comparison arguments to the sorting functions in CCArrayLabels.ml ~cmp instead of ~f ?
2018-10-13 19:09:35 -05:00
Simon Cruanes
2a9795090b test: improve perf by changing random gens 2018-10-13 18:52:22 -05:00
Simon Cruanes
e22a55668d style edit 2018-10-08 20:48:02 -05:00
Francois Berenger
1e21784ce1 added List.counts, related to List.count (#230)
- add `List.count_true_false` related to `List.count`
2018-10-08 20:43:06 -05:00
Hongchang Wu
3ec63feded Update authors 2018-10-05 00:55:32 -05:00
Hongchang Wu
e8617c4f05 Add CCResult.get_or_failwith 2018-10-05 00:55:32 -05:00
Simon Cruanes
3b1de9a1c8 chore(build): migrate to dune 1.0 2018-09-16 19:53:40 -05:00
Simon Cruanes
058c99b2a9
Create CODE_OF_CONDUCT.md 2018-09-16 22:09:01 +00:00
Metin Akat
b8fb429c0b Add myself to the authors file 2018-09-02 10:56:11 -05:00
Metin Akat
e447f5a1bc Version annotation of CCInt.( ** ) 2018-09-02 10:56:11 -05:00
Metin Akat
7d25684941 Support using ** for exponentiation 2018-09-02 10:56:11 -05:00
Simon Cruanes
13fe66c968 chore: update dune to use dune-config 2018-08-15 13:38:58 -05:00
Simon Cruanes
5f521f6fa2 chore: add 4.07 to travis 2018-08-07 08:39:43 -05:00
Simon Cruanes
9238daf7c3 fix: compat with 4.07 2018-08-07 03:23:11 -05:00
Simon Cruanes
d4a9b0a8d1 prepare for 2.3 2018-08-06 11:52:01 -05:00
Simon Cruanes
e530547356 feat(vector): add Vector.{filter,filter_map}_in_place 2018-07-26 04:39:06 -05:00
Simon Cruanes
c800250038 perf: tweaks in CCVector 2018-07-25 14:32:28 -05:00
Simon Cruanes
551c837398 test: more tests for vector 2018-07-25 14:32:28 -05:00
Simon Cruanes
e510e153f8 test: add more tests in vector 2018-07-25 14:32:28 -05:00
Simon Cruanes
1998ed5090 fix(vector): bugfix in filter 2018-07-25 14:32:28 -05:00
Simon Cruanes
7f1c6ae66f feat(intmap): add is_empty function 2018-06-11 20:10:45 -05:00
Simon Cruanes
aa4b2a4680 fix(build): remove [@inline] attributes since they break on 4.02.3 2018-06-11 19:01:06 -05:00
Simon Cruanes
3e5813d72f perf(hashtrie): use int64 for 64-bits branching factor and popcount
also update style
2018-06-05 00:31:56 -05:00
Simon Cruanes
5523ed428c feat(intmap): add CCIntMap.{filter,filter_map,merge} 2018-06-04 23:36:15 -05:00
Simon Cruanes
ca0521512f test(intmap): add some tests for CCIntMap, also improve style 2018-06-04 23:32:08 -05:00
Fabian
0c48cff2a1 Vector push benchmark: Push all values to the same container 2018-06-01 11:32:06 -05:00
Fabian
35065393c5 More benchmarks for immutable vectors
- Add Map as a reference, as this is the only efficient implementation
  in OCaml's standard library
- Add Clarity.Vector from the clarity library
- New benchmarks for set at index and push

What's missing:

- CCFun_vec.set (no implementation)
- CCRAL.push and Clarity.Vector.push were missing and implemented
  inefficiently in terms of append
2018-06-01 11:32:06 -05:00
Simon Cruanes
1adfc01cf0 fix small detail s in CCHeap 2018-05-29 11:17:11 -05:00
juloo
ba633d5d3c Add CCHeap.Make_from_compare (#225)
Allow to pass modules implementing `compare` without implementing `leq`
2018-05-29 11:15:38 -05:00
Simon Cruanes
1a4919af29 add relation ops CCList.{group_by,join,join_by,join_all_by,group_join_by} 2018-05-29 11:04:08 -05:00
juloo
cbeab54be4 Fix Int32 and Int64 operators are not visible (#224)
Fix visibility of Int32 and Int64 infix operators

Operators are defined in the Infix module.
This module was included in the .ml file but not in the .mli file
2018-05-29 09:57:04 -05:00
Simon Cruanes
8060980266 test(float): add some tests for FP min/max 2018-05-14 18:09:39 -05:00
Simon Cruanes
841dac234a fix(float): make Float.{min,max} compliant with revised IEEE754
closes #220
2018-05-14 18:07:38 -05:00
Simon Cruanes
e825bf2916 prepare for 2.2 2018-05-10 23:13:01 -05:00
Simon Cruanes
ba87e105a0 fix(arrayLabels): compatibility with 4.07 2018-05-07 13:41:19 -05:00
JPR
822b9177e1 Improving comments presentation 2018-05-07 08:46:38 -05:00
Simon Cruanes
becc1007c2 chore: reindent 2018-04-25 20:17:32 -05:00
Etienne Millon
b27acb9bd2 Add CCOpt.return_if 2018-04-25 20:16:23 -05:00
Etienne Millon
bbda79bbcc Add CCOpt.flatten 2018-04-24 17:03:45 -05:00
Kate
d8caef8c02 Add CCString.{,r}drop_while 2018-04-20 19:13:49 -05:00
Simon Cruanes
9e105a3fbc fix stupid typo 2018-04-12 15:47:23 -05:00
Simon Cruanes
ac314aefe7 chore: remove dead link in string doc 2018-04-12 12:11:49 -05:00
Simon Cruanes
7c56bd747e fix: compatibility for CCArrayLabels 2018-04-09 21:13:56 -05:00
Simon Cruanes
33d3ee114c add many missing functions to CCListLabels 2018-04-08 18:57:15 -05:00
Simon Cruanes
fac9f18031 test: consistency CCList{,Labels} 2018-04-08 18:54:40 -05:00
Simon Cruanes
960e704bb1 test: disable labels for tests
necessary for the consistency checks of Label modules
2018-04-08 18:48:54 -05:00
Simon Cruanes
b672eb0e6d fix(CCListLabels): fix @since annotation 2018-04-08 18:42:11 -05:00
Simon Cruanes
b47d622f66 test: add compatibility checks between CCArray{,Labels} 2018-04-08 18:40:40 -05:00
JPR
1727cc8199 Few Comments corrections 2018-04-08 18:39:23 -05:00
Simon Cruanes
f7d5177540 chore(doc): update ocamldoc markup to remove odoc warnings 2018-03-30 01:49:38 -05:00
Simon Cruanes
78d79c1317 chore: minor details in doc 2018-03-28 20:30:26 -05:00
Simon Cruanes
6e50ff41c6 prepare for 2.1 2018-03-28 20:26:17 -05:00
Simon Cruanes
c04ee13d6e fix(array): small change in signatures 2018-03-28 20:20:09 -05:00
Simon Cruanes
5986955fb6 feat(list): add {interleave,intersperse} (closes #191) 2018-03-28 20:10:14 -05:00
Simon Cruanes
89fc7f9c77 fix(string): compat 4.02 2018-03-28 20:09:05 -05:00
Simon Cruanes
55e92b4629 feat(mono): add dotted comparison operators for floats
closes #196
2018-03-28 19:53:02 -05:00
Simon Cruanes
972a6f2720 style: reindent 2018-03-28 19:46:57 -05:00
Simon Cruanes
b874ff9bf9 feat(Bijection): add more functions, add basic tests 2018-03-28 19:46:43 -05:00
Stavros Polymenis
89ce86eec0 Assert emptiness on both maps instead of raising custom exception.
https://github.com/c-cube/ocaml-containers/pull/211\#pullrequestreview-107483136
2018-03-28 19:35:20 -05:00
Stavros Polymenis
712b12d2f1 Add Bijection. https://en.wikipedia.org/wiki/Bijection
Discussion:
http://lists.ocaml.org/pipermail/containers-users/2018-February/000042.html
2018-03-28 19:35:20 -05:00
Simon Cruanes
fe23cb496c fix: strong type aliases in Random (closes #210) 2018-03-26 11:35:31 -05:00
Simon Cruanes
323a6bb40a test: regression test for #210 2018-03-26 11:35:31 -05:00
Simon Cruanes
f07cae6c82 detail 2018-03-18 21:53:24 -05:00
JPR
2c9ed9c550 Reverting CCFormat.mli comment 2018-03-15 23:09:31 -05:00
JPR
d18d9fb636 Comments - Style & typos fixing 2018-03-15 23:09:31 -05:00
Simon Cruanes
9b804b46a5 update readme 2018-03-14 18:21:41 -05:00
Simon Cruanes
7a22286ca1 use standard List.sort_uniq 2018-03-13 00:34:49 -05:00
Simon Cruanes
09d5b146f2 remove explicit dep on bytes in jbuild files 2018-03-12 22:45:48 -05:00
Simon Cruanes
2c5cda7e3d add ?margin parameter to CCFormat.ksprintf 2018-03-09 11:27:11 -06:00
Simon Cruanes
c578dd9583 minor fixes in doc 2018-03-08 17:54:20 -06:00
Simon Cruanes
4a9b41c3cd add various functions on CCUtf8_string 2018-03-07 00:17:48 -06:00
Simon Cruanes
79089677af CCFun_vec: implement missing functions 2018-03-07 00:08:34 -06:00
Simon Cruanes
ea4a4e4ffb utf8string: detect overlong encodings
- also, stronger tests for utf8string
2018-03-07 00:08:27 -06:00
Simon Cruanes
fe88bafe77 simplify CCFun_vec by removing transients, for now 2018-03-06 23:08:02 -06:00
Simon Cruanes
be76d6bf91 fixes in utf8_string: remove some forbidden cases 2018-03-06 23:07:59 -06:00
Simon Cruanes
640ab72bb2 bugfix in CCVector.slice_seq 2018-03-06 22:28:11 -06:00
Simon Cruanes
6b5735a318 fix compat with 4.02 2018-03-06 22:27:52 -06:00
Simon Cruanes
30fca7ae9e add CCUtf8_string with basic encoding and decoding functionalities 2018-03-06 22:16:17 -06:00
Simon Cruanes
524658fb0f add some doc 2018-03-06 10:15:33 -06:00
Nicola Mometto
8f4c1a24b7 default uses labelled argument 2018-03-06 10:12:20 -06:00
Nicola Mometto
68ad3d7408 Add <|> to CCLazy_list 2018-03-06 10:12:20 -06:00
JPR
8d3981d983 Adding CCNativeint 2018-02-28 08:43:14 -06:00
Simon Cruanes
98bb766de6 fix ccint 2018-02-27 21:37:28 -06:00
Simon Cruanes
da6d4a72fa enrich CCInt.Infix to get a uniform interface with CCInt{32,64} 2018-02-27 21:12:04 -06:00
Simon Cruanes
9f2ef2f461 add CCInt{32,64}.Infix 2018-02-27 21:09:35 -06:00
JPR
4a317e57c1 Correction containers.ml file 2018-02-27 20:34:37 -06:00
JPR
d8c16ec95b Adding CCInt32 module 2018-02-27 20:34:37 -06:00
Simon Cruanes
8777996817 point to appveyor build in readme 2018-02-27 10:40:11 -06:00
Jacques-Pascal Deplaix
f5f98c5e11 Fix appveyor 32bits 2018-02-27 15:25:22 +00:00
Jacques-Pascal Deplaix
a4dda4284c Add an appveyor test for 32bits windows 2018-02-27 12:59:29 +00:00
Simon Cruanes
5f0b648845 update printers names in containers.top (closes #201) 2018-02-26 19:47:24 -06:00
Jacques-Pascal Deplaix
a0d0cf9d88 Enable support for Travis CI and Appveyor 2018-02-25 09:54:01 -06:00
Jacques-Pascal Deplaix
001d330bb9 Fix ounit package name 2018-02-25 09:51:31 +00:00
Simon Cruanes
0fb25fac26 note that ccfun_vec is really experimental 2018-02-24 18:38:13 -06:00
Simon Cruanes
5ebebf4fd7 test deps are required when we run tests 2018-02-22 14:18:00 -06:00
Simon Cruanes
fe16608524 add CCHash.combine{5,6} 2018-02-21 10:21:36 -06:00
Simon Cruanes
22fce8e16f point to JST's blog post on poly compare 2018-02-19 14:16:23 -06:00
Simon Cruanes
ab378a98bb proper urls 2018-02-19 12:18:00 -06:00
Simon Cruanes
3c8869dd5b udpate readme to explain a bit more how to live with monomorphic ops 2018-02-19 12:16:14 -06:00
Simon Cruanes
145578f1d9
Merge pull request #195 from madroach/master
Add missing infix operators to `CCFloat`
2018-02-17 14:11:35 -06:00
Christopher Zimmermann
dabb7de24a Add infix operators to CCFloat 2018-02-17 21:05:36 +01:00
Simon Cruanes
62ba3c00af make CCInt64 compatible with Int64 (breaking!) (closes #192)
conversion functions are total, even when the bit widths do not
correspond. Returning options does not make sense in this context.
2018-02-17 10:25:57 -06:00
Simon Cruanes
580dc58979 remove junk file 2018-02-17 10:16:45 -06:00
Simon Cruanes
1640ee89f2 add missing signatures of CCArrayLabels (closes #193) 2018-02-17 10:16:08 -06:00
Simon Cruanes
bff1464560 use non empty lists as indexes in Fun_vec 2018-02-15 08:30:16 -06:00
Simon Cruanes
74d3b0f29f add BatVec to some benchmarks 2018-02-15 08:30:05 -06:00
Simon Cruanes
deb266e1b3 add CCFun_vec to benchmarks 2018-02-15 00:31:26 -06:00
Simon Cruanes
ccad1f3a2c wip: add CCFun_vec data structure for fast functional vectors 2018-02-15 00:31:26 -06:00
Simon Cruanes
02f8af6dbe
Merge pull request #189 from jpdeplaix/master
Fix #188 by adding lower bound on jbuilder
2018-02-14 11:57:43 -06:00
Jacques-Pascal Deplaix
7e08d7c7c1 Fix #188 2018-02-14 17:44:48 +00:00
Simon Cruanes
dd24feab60
Merge pull request #187 from emillon/ccfun-iterate
Add `CCFun.iterate`
2018-02-14 08:40:42 -06:00
Etienne Millon
f1adbcf2f3 Add CCFun.iterate
This adds a new `CCFun.iterate` function that computes the nth-iterate
of a function. That is, that function composed with itself n times.
2018-02-14 15:39:08 +01:00
Simon Cruanes
a4697946ac small typo in readme 2018-02-11 11:08:51 -06:00
Simon Cruanes
f78ee1bf92 add migration guide to the readme 2018-02-11 10:26:02 -06:00
Simon Cruanes
6b9f39d224 prepare for 2.0 2018-02-11 09:57:06 -06:00
Simon Cruanes
44f6c748aa improve test speed and update some doc 2018-02-11 09:52:27 -06:00
Simon Cruanes
35f9b32a5b add CCFormat.lazy_{or,force} for printing thunks 2018-02-09 14:20:46 -06:00
Simon Cruanes
d4fafab9b7 more tests 2018-02-07 20:38:01 -06:00
Simon Cruanes
b3c796176d reindent 2018-02-07 08:22:41 -06:00
Simon Cruanes
7510aaaa18
Merge pull request #186 from Fourchaux/master
move tests from mli files into ml files, update some doc
2018-02-07 08:22:10 -06:00
nathan moreau
3e2fbce3ee Comments - few changes 2018-02-07 14:36:03 +01:00
Simon Cruanes
24592bf926 CCFormat: fix support of unrecognized styles 2018-02-05 08:58:32 -06:00
Simon Cruanes
3ab9cd58e1 with compat >= 4.02, use Format.pp_print_text directly 2018-02-05 08:56:01 -06:00
Simon Cruanes
27c768eebf fix bug in CCRAL.drop (see #184) 2018-02-04 12:39:06 -06:00
Simon Cruanes
5814f23d16 add more tests to CCRAL and others 2018-02-04 12:38:17 -06:00
Simon Cruanes
72838b6ebc
Merge pull request #184 from rand00/master
Fixed bug in ``CCRAL.{drop,take_drop}`
2018-02-04 12:07:22 -06:00
Simon Cruanes
77cd903134
Merge branch 'master' into master 2018-02-04 12:06:44 -06:00
Simon Cruanes
6b48fe873e add another test 2018-02-04 12:05:58 -06:00
Simon Cruanes
9e51f8dc77
Merge pull request #185 from actionshrimp/cclist-repeat-reversed
Don't reverse twice in `CCList.repeat`
2018-02-04 11:57:05 -06:00
Dave Aitken
60596e5408 Update authors list 2018-02-04 17:56:07 +00:00
rand00
47c5c41a96 Added Rand to authors 2018-02-04 18:55:37 +01:00
rand00
cfb8e55eba CCRAL: drop: Added test for bugfix. 2018-02-04 18:54:56 +01:00
Dave Aitken
710266e09c Don't reverse twice in CCList.repeat 2018-02-04 17:34:30 +00:00
rand00
5b6b71373c CCRAL: Fixed bug in drop_tree_. 2018-02-04 17:30:29 +01:00
Simon Cruanes
04d10c2711 makefile help 2018-02-03 15:39:12 -06:00
Simon Cruanes
b340c3dc6c update deps 2018-02-03 15:36:53 -06:00
Simon Cruanes
a0a8954231 proper deps in dune 2018-02-03 15:36:53 -06:00
Simon Cruanes
29118df5f4 opam detail 2018-02-02 08:10:41 -06:00
Simon Cruanes
c382c1c2e5 prepare for 2.0+alpha2 2018-02-01 19:18:17 -06:00
Simon Cruanes
ddf709fc5b reindent code 2018-02-01 19:01:32 -06:00
Simon Cruanes
6ec2fdeb1e
Merge pull request #183 from Fourchaux/master
Adding more comments (thanks to @Fourchaux)
2018-02-01 19:01:19 -06:00
nathan moreau
312901550f Style - small corrections 2018-02-01 22:22:13 +01:00
Simon Cruanes
968edffc03 fix 2018-02-01 09:22:41 -06:00
Simon Cruanes
223647045a update jbuild file 2018-01-31 20:07:27 -06:00
Simon Cruanes
5720120fa1 more tests 2018-01-31 20:07:04 -06:00
Simon Cruanes
9f48725a06 remove qtest makefile and use a script instead 2018-01-31 08:49:52 -06:00
nathan moreau
775f86103e typo (CCString.mli) 2018-01-30 14:25:51 +01:00
nathan moreau
00b2638ae7 Adding more comments 2018-01-30 14:07:05 +01:00
Simon Cruanes
2939dcbf1d rename CCChar.{print,pp} and CCHeap.print (closes #181) 2018-01-29 21:15:24 -06:00
Simon Cruanes
3b8f7099cb style fixes 2018-01-28 10:31:26 -06:00
Simon Cruanes
364890ca36
Merge pull request #180 from FardaleM/CCHeap
Add `CCHeap.delete_{all,one}`
2018-01-28 10:30:20 -06:00
Simon Cruanes
c3e11ba31b
Merge pull request #182 from Fourchaux/master
Correcting test `CCList.tail_opt`
2018-01-28 10:03:50 -06:00
nathan moreau
e598aac764 Correcting test CCList.tail_opt 2018-01-28 16:36:27 +01:00
Fardale
8c2cb3f244 [CCHeap] delete_once -> delete_one 2018-01-28 12:39:44 +01:00
Fardale
8ac62ca04b [CCHeap] add delete_once 2018-01-28 12:37:21 +01:00
Fardale
50d970852e [CCHeap] update comment delete_all 2018-01-28 12:36:37 +01:00
Fardale
4e9eeb50e5 add @since for delete_all 2018-01-27 23:48:47 +01:00
Fardale
dcd975ce85 Merge remote-tracking branch 'perso/master' into CCHeap 2018-01-27 20:18:51 +01:00
Simon Cruanes
7198417fd1
Merge pull request #179 from Fourchaux/master
Adding `CCList.tail_opt`
2018-01-26 08:26:14 -06:00
Simon Cruanes
b8260c737f fix doc target in opam 2018-01-26 08:14:47 -06:00
Fardale
7c7f66cd7b Add CCHeap.delete_all 2018-01-26 09:31:29 +01:00
nathan moreau
b0d92f44ea Adding CCList.tail_opt 2018-01-26 09:25:17 +01:00
Simon Cruanes
2007d7ad37 remove dep on bytes 2018-01-22 08:28:44 -06:00
Simon Cruanes
b3d8eb67a6 update opam file 2018-01-21 20:06:13 -06:00
Simon Cruanes
a3fa07bc05 add @orbifx to authors 2018-01-21 17:50:49 -06:00
Simon Cruanes
7df23d0cca fix build for benchs 2018-01-21 17:09:30 -06:00
Simon Cruanes
c7483ade3b chore(makefile): detail, to always build lib before tests 2018-01-21 15:59:10 -06:00
Simon Cruanes
c53a550822 add optional deps in opam 2018-01-21 15:45:03 -06:00
Simon Cruanes
446ae9f26b prepare for 2.0 2018-01-21 15:38:34 -06:00
Simon Cruanes
bc27e60a81 reindent 2018-01-21 14:39:44 -06:00
Simon Cruanes
f98bcffaee
Merge pull request #178 from Fourchaux/master
Small typos & Style
2018-01-21 14:38:27 -06:00
nathan moreau
cde776f4ac Small typos 2018-01-21 20:29:51 +01:00
Simon Cruanes
f63bcee290
Merge pull request #177 from Fourchaux/master
Add comments and improve their style (from @Fourchaux)
2018-01-21 10:33:31 -06:00
nathan moreau
25f919070f Adding comments 2 2018-01-21 17:09:42 +01:00
nathan moreau
b04e097cf4 Adding comments 2018-01-21 16:16:35 +01:00
Simon Cruanes
829ceeb147
Merge pull request #176 from orbifx/master
Add `CCList.iteri2` and `CCList.foldi2`
2018-01-18 12:21:46 -06:00
Stavros Polymenis
796702a732 Add CCList.iteri2 and CCList.foldi2 2018-01-18 18:07:19 +00:00
Simon Cruanes
ec23d64550 remove junk file 2018-01-17 20:15:04 -06:00
Simon Cruanes
282f85a874 remove PARAM.min_size in CCPool 2018-01-17 20:12:50 -06:00
Simon Cruanes
0e26502008 wip: fix behavior of CCPool when min_size>0
problem is a deadlock occurs when some threads die (too early?)
when P.min_size>0
2018-01-17 20:11:14 -06:00
Simon Cruanes
bc622f636a fix tests for sequence 1.0 2018-01-17 20:04:03 -06:00
Simon Cruanes
ea4de9a618 udpate opam file 2018-01-15 08:44:18 -06:00
Simon Cruanes
564a804701 chore(travis): remove 4.01 from versions 2018-01-14 20:59:50 -06:00
Simon Cruanes
3be488a910 remove merlin file 2018-01-14 20:55:09 -06:00
Simon Cruanes
d97e1e7231 add script to generate jbuilder flags dynamically 2018-01-14 20:38:40 -06:00
Simon Cruanes
a307bb09f7 chore(jbuild): missing flag for optional sublibs, deps 2018-01-14 19:24:46 -06:00
Simon Cruanes
266c0c073e travis: need odoc for the doc 2018-01-14 19:24:46 -06:00
Simon Cruanes
fc329fa118 rename print to pp for Format printers (closes #153) 2018-01-14 17:58:20 -06:00
Simon Cruanes
19b2b7c7cc remove CCFlatHashtbl 2018-01-14 17:57:13 -06:00
Simon Cruanes
5119b69051 update travis script 2018-01-14 17:49:00 -06:00
Simon Cruanes
8f46fdb6d2 move to jbuilder (closes #165) 2018-01-14 17:43:56 -06:00
Simon Cruanes
2c9a1d70c9
Merge pull request #169 from jpdeplaix/CCMonomorphic
Add `CCMonomorphic`, make most optional arguments that rely on poly operators mandatory.
2018-01-14 17:34:53 -06:00
Jacques-Pascal Deplaix
88e4df4b12 Mention Pervasives.(==) as an alternative to CCEqual.physical 2018-01-14 23:33:39 +00:00
Jacques-Pascal Deplaix
aa0f34c5c9 Add CCEqual.physical 2018-01-14 23:33:39 +00:00
Jacques-Pascal Deplaix
3c808f397e Shadow the physical equality operator 2018-01-14 23:33:39 +00:00
Jacques-Pascal Deplaix
ff69945575 Shadow polymorphic functions in CCList 2018-01-14 23:33:39 +00:00
Jacques-Pascal Deplaix
35c2d0ed54 Fix tests 2018-01-14 23:32:13 +00:00
Jacques-Pascal Deplaix
0796a9a8d4 Fix compilation of benchs 2018-01-14 23:32:13 +00:00
Jacques-Pascal Deplaix
9622f6f6ff Avoid uses of the polymorphic operators 2018-01-14 23:32:13 +00:00
Jacques-Pascal Deplaix
35b4e772be Add Containers.Monomorphic and include it by default 2018-01-14 23:32:13 +00:00
Jacques-Pascal Deplaix
6d2063ded4 Internally shadow polymorphic operators and functions from Pervasives 2018-01-14 23:32:13 +00:00
Jacques-Pascal Deplaix
01a3b94ff9 Add a CCMonomorphic module shipped into a containers.monomorphic library 2018-01-14 23:32:13 +00:00
Simon Cruanes
631b33f62e fix doc 2018-01-14 16:21:59 -06:00
Simon Cruanes
4ffe1bbac3 make complexity of Array.lookup explicit (closes #174) 2018-01-06 17:14:00 -06:00
Simon Cruanes
1b9c014e25 use neat trick to alias properly type of Format 2018-01-06 08:24:42 -06:00
Simon Cruanes
53137a6183 prepare for 1.5.2 2018-01-04 11:13:26 -06:00
Simon Cruanes
a160ae3672 missing type aliases (see #173) 2018-01-04 08:53:33 -06:00
Simon Cruanes
d76831a8c3 changelog 2018-01-03 08:05:02 +01:00
Simon Cruanes
858ef0e90f missing extension of Format 2018-01-02 23:39:55 +01:00
351 changed files with 44969 additions and 21583 deletions

28
.github/workflows/format.yml vendored Normal file
View file

@ -0,0 +1,28 @@
name: format
on:
push:
branches:
- main
pull_request:
jobs:
format:
name: format
strategy:
matrix:
ocaml-compiler:
- '5.3'
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@main
- name: Use OCaml ${{ matrix.ocaml-compiler }}
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: ${{ matrix.ocaml-compiler }}
dune-cache: true
allow-prerelease-opam: true
- run: opam install ocamlformat.0.27.0
- run: opam exec -- make format-check

33
.github/workflows/gh-pages.yml vendored Normal file
View file

@ -0,0 +1,33 @@
name: github pages
on:
push:
branches:
- master # Set a branch name to trigger deployment
jobs:
deploy:
name: Deploy doc
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Use OCaml
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: '5.2'
dune-cache: false
- name: Deps
run: opam install odig containers containers-data
- name: Build
run: opam exec -- odig odoc --cache-dir=_doc/ containers containers-data
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./_doc/html/
destination_dir: dev
enable_jekyll: true

64
.github/workflows/main.yml vendored Normal file
View file

@ -0,0 +1,64 @@
name: Build and Test
on:
push:
branches:
- main
pull_request:
jobs:
run:
name: build
timeout-minutes: 15
strategy:
fail-fast: true
matrix:
os:
- ubuntu-latest
ocaml-compiler:
- '4.08'
- '4.10'
- '4.14'
- '5.3'
- 'ocaml-variants.5.0.0+options,ocaml-option-bytecode-only'
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@main
- name: Use OCaml ${{ matrix.ocaml-compiler }}
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: ${{ matrix.ocaml-compiler }}
dune-cache: true
allow-prerelease-opam: true
- run: opam install -t containers containers-data --deps-only
- run: opam exec -- dune build '@install'
- run: opam exec -- dune runtest --force --profile=release
compat:
name: build
timeout-minutes: 15
strategy:
fail-fast: true
matrix:
os:
- macos-latest
- ubuntu-latest
#- windows-latest
ocaml-compiler:
- '5.1'
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@main
- name: Use OCaml ${{ matrix.ocaml-compiler }}
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: ${{ matrix.ocaml-compiler }}
dune-cache: true
allow-prerelease-opam: true
- run: |
opam install -t containers --deps-only ;
opam install containers-data --deps-only # no test deps
- run: opam exec -- dune build '@install'
- run: opam exec -- dune runtest -j 1 -p containers --profile=release # test only core on non-ubuntu platform

11
.gitignore vendored
View file

@ -4,8 +4,15 @@ _build
*.native *.native
*.byte *.byte
.session .session
TAGS
*.docdir *.docdir
setup.* setup.*
qtest*
*.html *.html
.merlin
*.install
.ignore
_opam
*.exe
fuzz-*-input
fuzz-*-output
fuzz-logs/
doc/papers

21
.merlin
View file

@ -1,21 +0,0 @@
S src/core
S src/data/
S src/iter/
S src/sexp/
S src/threads/
S src/string
S benchs
S examples
S tests
B _build/src/**
B _build/benchs
B _build/examples
B _build/tests
PKG oUnit
PKG benchmark
PKG result
PKG threads
PKG threads.posix
PKG lwt
PKG qcheck
FLG -w +a-4-44-48-60@8

15
.ocamlformat Normal file
View file

@ -0,0 +1,15 @@
version = 0.27.0
profile=conventional
margin=80
if-then-else=k-r
parens-ite=true
parens-tuple=multi-line-only
sequence-style=terminator
type-decl=sparse
break-cases=toplevel
cases-exp-indent=2
field-space=tight-decl
leading-nested-match-parens=true
module-item-spacing=compact
quiet=true
parse-docstrings=false

View file

@ -1,23 +0,0 @@
#use "topfind";;
#thread
#require "result";;
#require "unix";;
#require "sequence";;
#directory "_build/src/core";;
#directory "_build/src/unix";;
#directory "_build/src/iter";;
#directory "_build/src/data";;
#directory "_build/src/sexp";;
#directory "_build/src/threads";;
#directory "_build/src/top/";;
#load "containers.cma";;
#load "containers_iter.cma";;
#load "containers_data.cma";;
#load "containers_unix.cma";;
#load "containers_sexp.cma";;
#load "containers_string.cma";;
#load "containers_top.cma";;
#thread;;
#load "containers_thread.cma";;
(* vim:syntax=ocaml:
*)

View file

@ -1,2 +0,0 @@
match_clause=2
with=2

View file

@ -1,37 +0,0 @@
language: c
env:
- OCAML_VERSION=4.01.0
- OCAML_VERSION=4.02.3
- OCAML_VERSION=4.04.2
- OCAML_VERSION=4.05.0
- OCAML_VERSION=4.05.0+flambda
- OCAML_VERSION=4.06.0
addons:
apt:
sources:
- avsm
packages:
- opam
# Caching may take a lot of space with so many ocaml versions
#cache:
# directories:
# - $HOME/.opam
before_install:
# Some opam boilerplate
- export OPAMYES=1
- export OPAMVERBOSE=1
- opam init
- opam switch ${OCAML_VERSION}
- eval `opam config env`
install:
# Install dependencies
- opam pin add --no-action containers .
- opam install oasis
- opam install --deps-only containers
script:
- ./configure --enable-unix --enable-thread --disable-tests --disable-bench
- make build
- opam install sequence qcheck qtest gen
- ./configure --enable-unix --enable-thread --enable-tests --enable-docs --disable-bench
- make test
- make doc

View file

@ -1,26 +0,0 @@
= Authors and contributors
- Simon Cruanes (`companion_cube`)
- Drup (Gabriel Radanne)
- Jacques-Pascal Deplaix
- Nicolas Braud-Santoni
- Whitequark (Peter Zotov)
- hcarty (Hezekiah M. Carty)
- struktured (Carmelo Piccione)
- Bernardo da Costa
- Vincent Bernardoff (vbmithr)
- Emmanuel Surleau (emm)
- Guillaume Bury (guigui)
- JP Rodi
- Florian Angeletti (@octachron)
- Johannes Kloos
- Geoff Gole (@gsg)
- Roma Sokolov (@little-arhat)
- Malcolm Matalka (`orbitz`)
- David Sheets (@dsheets)
- Glenn Slotte (glennsl)
- @LemonBoy
- Leonid Rozenberg (@rleonid)
- Bikal Gurung (@bikalgurung)
- Fabian Hemmer (copy)
- Maciej Woś (@lostman)

View file

@ -1,6 +1,561 @@
= Changelog
== 1.5
## 3.17
- feat: add `CCAtomic.update_cas`
- feat: add `Pvec.flat_map`
- faster `List.take_drop` thanks to a trick by nojb
- move to ocamlformat 0.27, format code
- test: enrich pvec test
- Patch CBor roundtrip property to hold for nan's too (thanks @jmid)
## 3.16
- breaking: Renamed predicate parameter of `take_while`, `rtake_while` from `p` to `f`, aligining it with pre-existing `drop_while`.
- feat: add `containers.leb128` library
- feat: add `CCFun.with_return`
- Added functions to the `Char` module to check common character properties.
- feat: add `CCVector.findi`
- fix: compat with OCaml 5.4
- fix: oob(!!) in CCHash.bytes
## 3.15
- Add `CCList.split_result` (#459)
- pretty printer in MultiSet
- `CCHeap`: building a heap from an almost-sorted sequence
- perf: `CCHeap`: building a heap from n elements is now in time O(n)
instead of O(n log n)
- perf: `CCHeap`: `filter` and `delete_all` are now in time O(n)
instead of O(n log n), and they ensure physical equality
(for `delete_all` this is a bugfix)
## 3.14
- predicate combinators: `and_pred` and `or_pred`
- feat `pp`: add a bunch of extensions
- Kleisli Composition Operator and Apply_or for option/result/fun (#455)
- add `CCByte_buffer.to_slice`
- add a byte slice type `CCByte_slice`
- add `cons_when` to `CCListLabels`
- add `(|||>)` and `||>` to `CCFun`
- `CCVector`: Add function foldi
- add `containers.pvec`, a persistent vector type.
- perf: use a monomorphic impl for `CCMonomorphic.{min,max}`
## 3.13.1
- list: TRMC was in 4.14, we can use it earlier
- fix insidious bug in CCList.flat_map linked to unspecified
evaluation order
- perf: use `concat_map` for `CCList.flat_map` on >= 5.1
(this also re-fixes the same bug in `CCList.flat_map` anyway)
## 3.13
- breaking: delete containers-thread (which was deprecated)
- breaking: pp: modify `Ext.t` so it takes surrounding value
- breaking: remove CCShims
- CCMultiMap: Rename functions find_left and find_right in the bidirectional multimap
to find_left_iter and find_right_iter respectively to reflect their usage,
and add new functions to replace the old find_left and find_right
that return a list of values rather than an iterator,
to make the signatures of CCMultiMap.S and CCMultiMap.BIDIR cohere.
Additionally, change the return type of
`S.find_iter` from `t -> key -> (value -> unit) -> unit` to `t -> key -> value iter`.
- CCList: add `unfold`
- CCBool: Add functions if_then and if_then_else
- CCList: remove some functions that are subsumed by the stdlib
- CCList: use TRMC for many functions on 5.1
- feat CCFunvec: add `fold_rev`
- add `Containers_pp.newline_or_spaces`
- cleanup: remove stubs for code always present on 4.08, rely on
newer functions in 5.1
- perf: accelerate `List.append` and `List.flat_map` on 5.1
- more warnings, more tests, cleanup dead code
- change COC to ocaml-coc
## 3.12
- add `containers.pp` sublibrary, with Wadler-style pretty printing combinators
- add `CCArray.{max,argmax,min,argmin}` and their _exn counterparts
- add `CCParse.take_until_success`
- add `Option.flat_map_l`
- add `CCSet.{find_first_map,find_last_map}`
- `CCHash`: native FNV hash for int64/int32
- fix bugs in CCParse related to `recurse` and `Slice`
- fix: fix Set.find_last_map on OCaml 4.03
- fix: make sure `Vector.to_{seq,gen}` captures the length initially
## 3.11
- official OCaml 5 support
- add `CCFun.(let@)` (if OCaml >= 4.08)
- add `CCHet.Tbl.{clear,reset}`
- fix(CCVector): concurrent modification safety in `resize_with`
- fix(CCVector): always obtain a copy of array before using unsafe_{set,get}
- CI: add ocaml 5.0.x
## 3.10
- `CCArray`: add `mapi_inplace`
- add sublibrary `containers.scc` for strongly connected components
- `CCSeq`: add `concat_map`
- `CCSeq`: add some missing function from 4.14
- add `CCInt64.{hash,hash_to_int64}`
- `Ref`: add `protect` function
- fix: include `Seq` in `CCSeq` for ocaml >= 4.07
## 3.9
- feat: add `Containers_cbor` module
- feat(CCInt32): add popcount function
- feat(CCInt64): add `popcount` operation
- CCBV:
* more extensive test suite
* use `bytes` underneath, not an array of integers
- add `containers_testlib`, removing qtest and ounit.
- `cbor`: use int64 as main int type
- fix: handle uppercase in string/hex
## 3.8
- add `Containers_bencode` for lightweight (de)ser
- perf(CCHash): improve a bit commutative hashing of arrays/lists
- perf(CCHash): only hash prefix of string/bytes
- feat(CCList): Add `Assoc.{keys,values,map_values}`
- feat(CCArray): add `CCArray.map_inplace`
- add `CCString.{to_hex,of_hex}`
- fix(Atomic): prevent race conditions under flambda, for now
## 3.7
- add `Format.styling`
- make `Format` compatible with OCaml 5.0, using Stag for colors
- new preprocessor, compatible with merlin, using `[@@@ifge 4.12]`-style pragmas
- feat: add `Byte_buf`, a byte buffer.
- add `CCSeq.{zip_i,of_string}`
- add `CCResult.opt_map` to simplify result function application over optionals (#397)
- add shims for Atomic and Unit
- expose `CCParse.pos` to get current pos; improve perf
- add `CCVector.resize_with` and `CCVector.resize_with_init`, tests and doc (#389)
- add `CCVector.insert`
- update dune to 1.10, condition some rules to unix
- perf: reduce allocations in `CCSeq.to_array`
- fix asymptotic behavior of resize functions in `CCVector`
- fix: rely on `either` compatibility library
## 3.6.1
- use `either` compatibility library instead of shims
## 3.6
- rename `CCOpt` to `CCOption` and deprecate `CCOpt`
- add iterator functions to `CCIO`
- `CCOrd`: add `poly`, deprecate `compare`
- add `CCIO.File.walk_iter`
- `CCParse`: heavy refactoring, many new functions
* backtracking by default
* add `slice` and the ability to recurse on them
* expose Position module, add `or_`, `both`, `lookahead`, `U.bool`
* example Sexpr parser, and a test
* example and test of an IRC log parser
- fix bug in `CCIO.read_lines_seq`
## 3.5.1
- fix bug in `CCIO.read_lines_seq` (backported from 3.6)
## 3.5
- add `CCHash.map` and `CCHash.bytes`
- CCIO: add many `Seq.t` based functions
- CCUtf8string: add `{make,empty,of_uchar}`
- add `CCFormat.{const_string,opaque}`
- add `CCOpt.{some,none}`
- CCFormat: expose `ANSI_codes` module
- CCBV: add `equal`, refactor for performance and readability
- CCList: add `{sorted_diff_uniq,sorted_mem,sorted_diff,sorted_remove}`
- fix(bv): index error in union
- test: add some property tests on `Csexp/Canonical_sexp`
- bv: add more tests, including regression for #370
## 3.4
- Add `CCOpt.get_exn_or` and deprecate `CCOpt.get_exn`
- CCRAL: add `get_and_remove_exn` operation
- CCString: add `CCString.uniq`
- refactor `CCHash` to use FNV in many combinators
- CCInt: improve perf by using a single implementation of popcount using int64
- fix: CCRAL.remove does not always remove
- fix(sexp): re-export the `loc` type to the functor's argument's type
- refactor and clarify `cutoff` in `String.edit_distance`
- fix(CCInt): make sure hash is always positive
- big upgrade to CI thanks to @Fardale
## 3.3
- feat: add code-generator for optimal bitfields; add tests
- new Canonical sexpr module with printer and parser
- CCSeq: Add `for_all` and `exists`
- feat(sexp): expose last location in decoder
- feat(CCChar): add CCChar.Infix
- feat(CCString): add CCString.foldi
- feat(CCFormat): add `string_lines` combinator
- feat(CCList): update with regards to `partition_map`
- add `CCList.cons'`
- implement {of,add}_*_with family of function in `CCMap` with update (#352)
- add `CCMap.of_{list,iter,seq}_with` functions
- add `CCHashtbl.{of,add}_{list,seq,iter}_with`
- Fix integer overflow warning on jsoo (#346)
- updated fuzzer scripts
### Containers-thread
- refactor(pool): less locking, fix deadlock, more parallelism
- feat(pool): keep one idle thread
- small optim in `Pool.sequence_a`
## 3.2
- add CCEither module
- add `CCList.chunks`
- add iter/seq functions to `CCString`
- add `CCList.reduce` (resolves #305)
- fix: in `CCInt` pick popcount at runtime on 64 bits
- fix: in shims, use configurator properly to determine int size
- in `CCFormat`, add `append`, `append_l`, infix `++` for sequencing,
`space`, `break`, `cut`
- fix: in `CCSexp`, handle non-ascii escapes in strings
- `CCUtf8_string`: add and expose `uchar_to_bytes`
- enable auto deploy of doc
- improve CI: test core on non ubuntu platform, test all on ubuntu
- update readme
- CCImmutArray: add tests (#344)
- add fuzzing (#339)
- add stronger test to compare with uutf in ccutf8string
## 3.1
- add `List.combine_chop` and corresponding `(and&)` synchronized product
- chore: remove travis to use github CI instead
- add `CCList.mguard` function for list comprehensions
- add some basic tests to CCMutHeap
- un-specify order of elements in `CCMap.to_list`
- Move definition of `CCMap.update` so that it is shadowed by Stdlib.Map.update
- fix(intmap): order of arguments for the HO param should be stable
- feat(containers-data): add `CCMutHeap` mutable heap with increase/decrease
## 3.0.1
- fix build on 32 bits architectures
## 3.0
### Breaking changes
see https://github.com/c-cube/ocaml-containers/issues/290 for a summary of
a subset of these changes.
packaging:
- split the library into separate packages
`containers`, `containers-data`, and `containers-thread`.
- delete `containers.iter` and merge parts of it into `containers-data`;
- move `CCSexp` into the core library, remove `containers.sexp`.
api:
- remove slice APIs in string and array.
- change pp functions to take unit printer for sep/stop/start (#295)
- CCPair: use more standard name for some map functions (#316)
- add CCSeq module, mostly adapted from `CCKlist`
- remove `CCKlist` from everywhere
- CCGraph: remove deprecated module and function
- rename `<op>_std_seq` to `<op>_seq`, making `Seq.t` the standard everywhere;
remove the old `<op>_seq` that were previously
deprecated in favor of `<op>_iter`.
- CCVector: rename `shrink` into `truncate`
- CCVector: rename `remove to CCVector.remove_unordered`
- CCList: make mem compatible with the Stdlib by making `?eq` optional
- CCVector: rename `filter'` into `filter_in_place`
### Other changes
- CI: add github actions in addition to travis
- feat: add infix operators to `String`
- feat: add opt.bind
- CCResult: add `<$>` operator
- CCResult: add `get_lazy`
- put infix operators in `Infix` module, then include it
- ccnativeint: complete CCNativeint with regards to CCInt
- Int64: complete CCInt64 with regards to CCInt
- CCInt32: complete CCInt32 with regards to CCInt
- implement `CCInt.sign` using `CCInt.compare`
- CCInt: include module Int for ocaml >= 4.08
- CCInt: add `of_float`
- CCInt: add `of_string_exn`
- add `CCResult.get_lazy`
- add `Int.popcount` operator
- CCFloat: add `pi`
- CCFloat: add `of_string_opt`
- fix: expose `always_eq`/`never_eq` in `CCEqual`
- string: add optional `cutoff` arg on `String.edit_distance`
- CCVector: add `remove_and_shift`
- CCArray: add optional argument eq to mem
- CCSexp: Escape empty atoms
- substitute 'Pervasives' with 'Stdlib'
- CCFormat: add `exn` combinator
- IO: add `copy_into` for transferring data between channels
- Extend benchmark: `to_array`, cons and `cons_fold`
- Extend benchmark: Sek, iter and pop
- benchmark for memory usage of data structures
And many, many bugfixes.
## 2.8.1
- add missing `CCVector.of_iter`
## 2.8
### Breaking:
- bump minimum version of OCaml to 4.03, drop deps `{result,uchar}`
- deprecate `{of,to}_seq` a bit everywhere
- deprecate `CCKList` as it's subsumed by `Seq`
- feat: on `>= 4.08`, support let+ and let* operators
- feat(list): add indexed functions and `fold_on_map`
- refactor: also port `CCGraph` to iter
- feat: add `{to,of,add}_{iter,std_seq}` where relevant
- feat(unix): add `ensure_session_leader` and add some docs
- feat(pool): add infix operators on futures
- fix(pp): improve printing of hashtables
- feat: add `monoid_product` to Array and Vector
- improved gc behavior for `CCvector`
- deprecate `CCVector.fill_empty_slots_with`
- `CCVector.shrink_to_fit` to limit memory usage
- add `CCVector.clear_and_reset`
- feat(sexp): expose `parse_string_list` and the list decoder
- add `CCUnix.with_temp_dir` function
- deprecate `CCOpt.to_seq`, provide `to_iter` instead
- add `CCOpt.value` to improve compat with `Stdlib.Option`
- add `CCVector.mapi`
- fix: restore `CCSexp.atom` and `list` which was lost in 2.7
- fix(sexp): set location properly when parsing a file
- fix: properly alias to `CCChar` in containers.ml
- use older dune dialect
- remove unlabel, remove all traces of Result
- require dune configurator explicitly in opam
- Re-enable mdx tests
- fix benchs so they don't depend on clarity and they compile again
## 2.7
- deprecate CCKList in favor of the standard Seq
- CCIO: add `_gen` suffixes to some functions
- ccsexp: provide ability to annotate parsed S-exprs with their position
- ccsexp: functorize the parser/printer
- ccsexp: support `#;` for commenting a sexp
- fix: remove dep from vec to list
- add `to_string` to many modules (#270)
- add `CCDeque.{remove_*;update_*}`,` CCDeque.{*_opt}`
- add `CCDeque.{filter,filter_map}`
- add `CCDeque.filter_in_place`
- add `CCBool.{to,of}_int`
- add `Result.flatten_l` to turn a list of results into a result of list
- refactor: remove stdlib's code, simple reimplementation of `Stdlib.Fun`
- add `CCArray.Infix`
- Document behaviour of `Fun.finally` when finaliser raises
- travis: test on OCaml 4.09, too.
- more docs for IO
## 2.6.1
bugfix release:
- fix(parse): error in `many`
- chore: add 4.08 to travis
- fix `Containers.Stdlib` on OCaml 4.07
## 2.6
- introduce shim modules for 4.08 compat
- remove reference to sequence, use `iter` instead for tests
- add `remove` function to het map/tbl
- missing type annotation for specializing int.compare
- doc: fix bad example in CCIO
- use `iter`, not `sequence`, in tests
- fix: use same evaluation order as stdlib for `CCList.init`
- fix: make `Array.random_choose` fail on empty array at creation time
- breaking: make `Array.random_choose` raise invalid_arg instead of not_found
- migrate readme to .md, using mdx to test it
## 2.5
- perf: annotate types in monomorphic/float/int to help specialize builtins
- use GADT to discard impossible case on `CCFQueue` (@dinosaure).
- fix(funvec): expose `pop`, fix off by one error
## 2.4.1
- revert some compatibility-breaking changes in label modules
## 2.4
### breaking:
- rename `Random.sample_without_{replacement,duplicates}`
### Features
- add `CCResult.iter_err`
- add `CCEqual.{always,never}_eq`
- add `containersLabels.ml`, generate unlabelled interfaces from labelled ones
- add `CCEqualLabels`
- add `CCArray_sliceLabels`
- add `CCStringLabels`
- add `CCResult.get_or_failwith`
- add `CCInt.( ** )` for integer exponentiation
- add `List.counts`, related to `List.count` (#230)
- migrate to dune
- migrate to opam2
- add CODE_OF_CONDUCT.md
### Fixes
- #235: release memory in vector/ringbuffer (thanks to @copy)
- remove spurious `Labels` module
- doc: fix small inaccuracy in comments and API
- test: improve perf by changing random gens
## 2.3
- feat(vector): add `Vector.{filter,filter_map}_in_place`
- perf(hashtrie): use int64 for 64-bits branching factor and popcount
- feat(intmap): add `CCIntMap.{filter,filter_map,merge,is_empty}`
- Add `CCHeap.Make_from_compare` (#225)
- add relational ops `CCList.{group_by,join,join_by,join_all_by,group_join_by}`
- fix(float): make `Float.{min,max}` compliant with revised IEEE754
- fix(build): remove `[@inline]` attributes since they break on 4.02.3
- Fix Int32 and Int64 operators that are not visible (#224)
- some performance tweaks in Vector
- test(float): add some tests for FP min/max
## 2.2
- Improving comments presentation
- Add `CCOpt.return_if`
- Add `CCOpt.flatten`
- Add `CCString.{,r}drop_while`
- add many missing functions to `CCListLabels`
- test: consistency `CCList{,Labels}`
- fix(arrayLabels): compatibility with 4.07
- fix: compatibility for CCArrayLabels
- test: add compatibility checks between `CCArray{,Labels}`
## 2.1
- make `CCInt64` compatible with `Int64` (breaking!) (closes #192)
- Add `CCBijection` in containers.data
- feat(mono): add dotted comparison operators for floats
- add `?margin` parameter to `CCFormat.ksprintf`
- add `CCUtf8_string` with basic encoding and decoding functionalities
- Add `CCLazy_list.<|>`
- Adding `CCNativeint`
- enrich `CCInt.Infix` to get a uniform interface with `CCInt{32,64}`
- add `CCInt{32,64}.Infix`
- Adding CCInt32 module
- add `CCHash.combine{5,6}`
- Add infix operators to CCFloat
- feat(list): add `{interleave,intersperse}` (closes #191)
- add missing signatures of `CCArrayLabels` (closes #193)
- Add CCFun.iterate
- add experimental `CCFun_vec` data structure for fast functional vectors
- fix: strong type aliases in Random (closes #210)
- use standard `List.sort_uniq`
- remove explicit dep on `bytes` in jbuild files
- update printers names in containers.top (closes #201)
- Enable support for Travis CI and Appveyor
- test deps are required when we run tests
- point to JST's blog post on poly compare
## 2.0
### breaking
- move to jbuilder (closes #165), requiring at least OCaml 4.02
- become defensive w.r.t polymorphic operators:
* Internally shadow polymorphic operators and functions from Pervasives
by `include CCMonomorphic` in `Containers` module
* Shadow the physical equality operator
* Shadow polymorphic functions in `CCList`
- rename `print` to `pp` for Format printers (closes #153, #181)
- remove `CCFlatHashtbl`
### others
- many typos and style fixes (from Fourchaux)
- Add `CCList.iteri2` and `CCList.foldi2`
- remove `PARAM.min_size` in `CCPool`
- Add `CCEqual.physical`
- Avoid uses of the polymorphic operators
- Add a `CCMonomorphic` module shipped into a `containers.monomorphic` library
- make complexity of `Array.lookup` explicit (closes #174)
- add `CCFormat.lazy_{or,force}` for printing thunks
- now that ocaml >= 4.02 is required, use `Format.pp_print_text` directly
- add `CCHeap.delete_{one,all}`
- add `CCList.tail_opt`
- remove qtest makefile and use a script instead
- add many tests
- fix bug in `CCRAL.drop` (see #184)
- `CCFormat`: fix support of unrecognized styles
- fix bug: don't reverse twice in `CCList.repeat`
## 1.5.1, 1.5.2
- re-export `Format` types and functions in `CCFormat`
## 1.5
- have `CCList.{get,insert,set}_at_idx` work with negative indices - have `CCList.{get,insert,set}_at_idx` work with negative indices
- Add CCCache.add - Add CCCache.add
@ -22,7 +577,7 @@
- update doc of `CCList.cartesian_product`, which returns results in unspecified order (close #154) - update doc of `CCList.cartesian_product`, which returns results in unspecified order (close #154)
- fix containers.top (closes #155) - fix containers.top (closes #155)
== 1.4 ## 1.4
- add `CCMap.union` - add `CCMap.union`
- add `CCRef.swap` - add `CCRef.swap`
@ -42,7 +597,7 @@
- More tests for CCVector.append and CCVector.append_array - More tests for CCVector.append and CCVector.append_array
- assertions and cleanup in `CCPool` - assertions and cleanup in `CCPool`
== 1.3 ## 1.3
- deprecate `CCBool.negate` - deprecate `CCBool.negate`
- add `CCString.compare_natural` (closes #146) - add `CCString.compare_natural` (closes #146)
@ -60,7 +615,7 @@
- cleanup and refactor of `CCRingBuffer` (see #126). Add strong tests. - cleanup and refactor of `CCRingBuffer` (see #126). Add strong tests.
- add rich testsuite to `CCIntMap`, based on @jmid's work - add rich testsuite to `CCIntMap`, based on @jmid's work
== 1.2 ## 1.2
- make many modules extensions of stdlib (close #109) - make many modules extensions of stdlib (close #109)
the modules are: `String List ListLabels Array ArrayLabels Char Random` the modules are: `String List ListLabels Array ArrayLabels Char Random`
@ -94,9 +649,9 @@
- build unix support by default - build unix support by default
- bugfix and test for `CCZipper.is_focused` (closes #102) - bugfix and test for `CCZipper.is_focused` (closes #102)
- use boxes in `CCFormat.Dump` for tuples - use boxes in `CCFormat.Dump` for tuples
- update header, and use more `(==)` in `CCIntMap` - update header, and use more `(##)` in `CCIntMap`
== 1.1 ## 1.1
**bugfixes**: **bugfixes**:
@ -117,7 +672,7 @@
- remove CCError from tutorial - remove CCError from tutorial
- merge tutorial into readme, cleanup - merge tutorial into readme, cleanup
== 1.0 ## 1.0
See https://github.com/c-cube/ocaml-containers/issues/84 for an overview. See https://github.com/c-cube/ocaml-containers/issues/84 for an overview.
@ -180,14 +735,14 @@ See https://github.com/c-cube/ocaml-containers/issues/84 for an overview.
- add doc for `of_list` in relevant modules (close #85) - add doc for `of_list` in relevant modules (close #85)
- bugfix: do not use `Sequence.flatMap` (close #90) - bugfix: do not use `Sequence.flatMap` (close #90)
== 0.22 ## 0.22
- threads/CCLock: add `try_with_lock` to wrap `Mutex.try_lock` - threads/CCLock: add `try_with_lock` to wrap `Mutex.try_lock`
- Add `CCMultiSet.remove_all` - Add `CCMultiSet.remove_all`
- document errors in `CCIO` (close #86) - document errors in `CCIO` (close #86)
- use the new qtest/qcheck - use the new qtest/qcheck
== 0.21 ## 0.21
- (breaking) make default `start`/`stop` arguments empty in printers (#82) - (breaking) make default `start`/`stop` arguments empty in printers (#82)
@ -196,13 +751,13 @@ See https://github.com/c-cube/ocaml-containers/issues/84 for an overview.
- add `CCArray.Sub.to_list` - add `CCArray.Sub.to_list`
- add `CCArray.{sorted,sort_indices,sort_ranking}` (closes #81) - add `CCArray.{sorted,sort_indices,sort_ranking}` (closes #81)
- handle '\r` in CCSexpM (fixes #83) - handle `\r` in CCSexpM (fixes #83)
- add alias `Containers.IO` - add alias `Containers.IO`
- bugfixes in `CCArray.Sub` - bugfixes in `CCArray.Sub`
- bugfix + tests for `CCArray.Sub.sub` - bugfix + tests for `CCArray.Sub.sub`
- disable parallel build to support cygwin - disable parallel build to support cygwin
== 0.20 ## 0.20
- bugfix in `CCArray.equal` - bugfix in `CCArray.equal`
- fix `CCString.*_ascii`; add `CCChar.{upper,lower}case_ascii` - fix `CCString.*_ascii`; add `CCChar.{upper,lower}case_ascii`
@ -216,7 +771,7 @@ See https://github.com/c-cube/ocaml-containers/issues/84 for an overview.
- more general types for `CCArray.{for_all2,exists2}` - more general types for `CCArray.{for_all2,exists2}`
- more general type for `CCResult.map_or` - more general type for `CCResult.map_or`
== 0.19 ## 0.19
- add regression test for #75 - add regression test for #75
- Fix `CCString.Split.{left,right}` (#75) - Fix `CCString.Split.{left,right}` (#75)
@ -228,7 +783,7 @@ See https://github.com/c-cube/ocaml-containers/issues/84 for an overview.
- add `CCstring.of_char` - add `CCstring.of_char`
- update headers - update headers
== 0.18 ## 0.18
- update implem of `CCVector.equal` - update implem of `CCVector.equal`
- add `CCOpt.get_or` with label, deprecates `get` - add `CCOpt.get_or` with label, deprecates `get`
@ -239,13 +794,13 @@ See https://github.com/c-cube/ocaml-containers/issues/84 for an overview.
- add `Lazy_list.filter` - add `Lazy_list.filter`
- add `CCList.range_by` - add `CCList.range_by`
== 0.17 ## 0.17
=== potentially breaking ### potentially breaking
- change the semantics of `CCString.find_all` (allow overlaps) - change the semantics of `CCString.find_all` (allow overlaps)
=== Additions ### Additions
- add `CCString.pad` for more webscale - add `CCString.pad` for more webscale
- add `(--^)` to CCRAl, CCFQueue, CCKlist (closes #56); add `CCKList.Infix` - add `(--^)` to CCRAl, CCFQueue, CCKlist (closes #56); add `CCKList.Infix`
@ -274,7 +829,7 @@ See https://github.com/c-cube/ocaml-containers/issues/84 for an overview.
- add `CCImmutArray` into containers.data - add `CCImmutArray` into containers.data
- add `CCList.Assoc.remove` - add `CCList.Assoc.remove`
=== Fixes, misc ### Fixes, misc
- Make `CCPersistentHashtbl.S.merge` more general. - Make `CCPersistentHashtbl.S.merge` more general.
- optimize KMP search in `CCString.Find` (hand-specialize code) - optimize KMP search in `CCString.Find` (hand-specialize code)
@ -292,9 +847,9 @@ others:
- add an `IO` section to the tutorial - add an `IO` section to the tutorial
- enable `-j 0` for ocamlbuild - enable `-j 0` for ocamlbuild
== 0.16 ## 0.16
=== breaking ### breaking
- change the signature of `CCHeap.{of_gen,of_seq,of_klist}` - change the signature of `CCHeap.{of_gen,of_seq,of_klist}`
- change the API of `CCMixmap` - change the API of `CCMixmap`
@ -302,18 +857,18 @@ others:
- optional argument `~eq` to `CCGraph.Dot.pp` - optional argument `~eq` to `CCGraph.Dot.pp`
- rename `CCFuture` into `CCPool` - rename `CCFuture` into `CCPool`
=== deprecations ### deprecations
- deprecate `containers.bigarray` - deprecate `containers.bigarray`
- deprecate `CCHashtbl.{Counter,Default}` tables - deprecate `CCHashtbl.{Counter,Default}` tables
- deprecate `CCLinq` in favor of standalone `OLinq` (to be released) - deprecate `CCLinq` in favor of standalone `OLinq` (to be released)
=== bugfixes ### bugfixes
- fix wrong signature of `CCHashtbl.Make.{keys,values}_list` - fix wrong signature of `CCHashtbl.Make.{keys,values}_list`
- missing constraint in `CCSexpM.ID_MONAD` - missing constraint in `CCSexpM.ID_MONAD`
=== new features ### new features
- add a tutorial file - add a tutorial file
- add a printer into CCHeap - add a printer into CCHeap
@ -348,14 +903,14 @@ others:
- update `examples/id_sexp` so it can read on stdin - update `examples/id_sexp` so it can read on stdin
- add `CCList.fold_map2` - add `CCList.fold_map2`
== 0.15 ## 0.15
=== breaking changes ### breaking changes
- remove deprecated `CCFloat.sign` - remove deprecated `CCFloat.sign`
- remove deprecated `CCSexpStream` - remove deprecated `CCSexpStream`
=== other changes ### other changes
- basic color handling in `CCFormat`, using tags and ANSI codes - basic color handling in `CCFormat`, using tags and ANSI codes
- add `CCVector.ro_vector` as a convenience alias - add `CCVector.ro_vector` as a convenience alias
@ -371,22 +926,22 @@ others:
- bugfix: forgot to export `{Set.Map}.OrderedType` in `Containers` - bugfix: forgot to export `{Set.Map}.OrderedType` in `Containers`
== 0.14 ## 0.14
=== breaking changes ### breaking changes
- change the type `'a CCParse.t` with continuations - change the type `'a CCParse.t` with continuations
- add labels on `CCParse.parse_*` functions - add labels on `CCParse.parse_*` functions
- change semantics of `CCList.Zipper.is_empty` - change semantics of `CCList.Zipper.is_empty`
=== other changes ### other changes
- deprecate `CCVector.rev'`, renamed into `CCVector.rev_in_place` - deprecate `CCVector.rev'`, renamed into `CCVector.rev_in_place`
- deprecate `CCVector.flat_map'`, renamed `flat_map_seq` - deprecate `CCVector.flat_map'`, renamed `flat_map_seq`
- add `CCMap.add_{list,seqe` - add `CCMap.add_{list,seqe`
- add `CCSet.add_{list,seq}` - add `CCSet.add_{list,seq}`
- fix small uglyness in `Map.print` and `Set.print` - fix small ugliness in `Map.print` and `Set.print`
- add `CCFormat.{ksprintf,string_quoted}` - add `CCFormat.{ksprintf,string_quoted}`
- add `CCArray.sort_generic` for sorting over array-like structures in place - add `CCArray.sort_generic` for sorting over array-like structures in place
- add `CCHashtbl.add` mimicking the stdlib `Hashtbl.add` - add `CCHashtbl.add` mimicking the stdlib `Hashtbl.add`
@ -426,9 +981,9 @@ others:
- bugfix in hashtable printing - bugfix in hashtable printing
- bugfix in `CCKList.take`, it was slightly too eager - bugfix in `CCKList.take`, it was slightly too eager
== 0.13 ## 0.13
=== Breaking changes ### Breaking changes
- big refactoring of `CCLinq` (now simpler and cleaner) - big refactoring of `CCLinq` (now simpler and cleaner)
- changed the types `input` and `ParseError` in `CCParse` - changed the types `input` and `ParseError` in `CCParse`
@ -436,12 +991,12 @@ others:
- change the exceptions in `CCVector` - change the exceptions in `CCVector`
- change signature of `CCDeque.of_seq` - change signature of `CCDeque.of_seq`
=== Other changes ### Other changes
- add module `CCWBTree`, a weight-balanced tree, in `containers.data`. - add module `CCWBTree`, a weight-balanced tree, in `containers.data`.
- add module `CCBloom` in `containers.data`, a bloom filter - add module `CCBloom` in `containers.data`, a bloom filter
- new module `CCHashTrie` in `containers.data`, HAMT-like associative map - new module `CCHashTrie` in `containers.data`, HAMT-like associative map
- add module `CCBitField` in `containers.data`, a safe abstraction for bitfields of < 62 bits - add module `CCBitField` in `containers.data`, a safe abstraction for bitfields of `< 62 bits`
- add module `CCHashSet` into `containers.data`, a mutable set - add module `CCHashSet` into `containers.data`, a mutable set
- add module `CCInt64` - add module `CCInt64`
- move module `RAL` into `containers.data` as `CCRAL` - move module `RAL` into `containers.data` as `CCRAL`
@ -488,14 +1043,14 @@ others:
- new implementation for `CCDeque`, more efficient - new implementation for `CCDeque`, more efficient
- update makefile (target devel) - update makefile (target devel)
== 0.12 ## 0.12
=== breaking ### breaking
- change type of `CCString.blit` so it writes into `Bytes.t` - change type of `CCString.blit` so it writes into `Bytes.t`
- better default opening flags for `CCIO.with_{in, out}` - better default opening flags for `CCIO.with_{in, out}`
=== non-breaking ### non-breaking
NOTE: use of `containers.io` is deprecated (its only module has moved to `containers`) NOTE: use of `containers.io` is deprecated (its only module has moved to `containers`)
@ -516,7 +1071,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- fix: use the proper array module in `CCRingBuffer` - fix: use the proper array module in `CCRingBuffer`
- bugfix: `CCRandom.float_range` - bugfix: `CCRandom.float_range`
== 0.11 ## 0.11
- add `CCList.{remove,is_empty}` - add `CCList.{remove,is_empty}`
- add `CCOpt.is_none` - add `CCOpt.is_none`
@ -538,7 +1093,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- add `CCList.Set.{add,remove}` - add `CCList.Set.{add,remove}`
- fix doc of `CCstring.Split.list_` - fix doc of `CCstring.Split.list_`
== 0.10 ## 0.10
- add `containers.misc.Puf.iter` - add `containers.misc.Puf.iter`
- add `CCString.{lines,unlines,concat_gen}` - add `CCString.{lines,unlines,concat_gen}`
@ -553,7 +1108,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- remove `containers.pervasives`, add the module `Containers` to core - remove `containers.pervasives`, add the module `Containers` to core
- bugfix in `CCFormat.to_file` - bugfix in `CCFormat.to_file`
== 0.9 ## 0.9
- add `Float`, `Ref`, `Set`, `Format` to `CCPervasives` - add `Float`, `Ref`, `Set`, `Format` to `CCPervasives`
- `CCRingBuffer.append` (simple implementation) - `CCRingBuffer.append` (simple implementation)
@ -573,7 +1128,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- add `CCSet` module in core/ - add `CCSet` module in core/
- add `CCRef` module in core/ - add `CCRef` module in core/
== 0.8 ## 0.8
- add `@Emm` to authors - add `@Emm` to authors
- refactored heavily `CCFuture` (much simpler, cleaner, basic API and thread pool) - refactored heavily `CCFuture` (much simpler, cleaner, basic API and thread pool)
@ -581,7 +1136,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- merged `test_levenshtein` with other tests - merged `test_levenshtein` with other tests
- Add experimental rose tree in `Containers_misc.RoseTree`. - Add experimental rose tree in `Containers_misc.RoseTree`.
- remove a lot of stuff from `containers.misc` (see `_oasis` for details) - remove a lot of stuff from `containers.misc` (see `_oasis` for details)
- `make devel` command, activating most flags, for developpers (see #27) - `make devel` command, activating most flags, for developers (see #27)
- use benchmark 1.4, with the upstreamed tree system - use benchmark 1.4, with the upstreamed tree system
- test `ccvector.iteri` - test `ccvector.iteri`
- add `CCFormat` into core/ - add `CCFormat` into core/
@ -596,9 +1151,9 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- `CCHashtbl.{keys,values}_list` - `CCHashtbl.{keys,values}_list`
- more accurate type for `CCHashtbl.Make` - more accurate type for `CCHashtbl.Make`
== 0.7 ## 0.7
=== breaking ### breaking
- remove `cgi`/ - remove `cgi`/
- removed useless Lwt-related module - removed useless Lwt-related module
@ -606,11 +1161,11 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- split the library into smaller pieces (with `containers.io`, `containers.iter`, - split the library into smaller pieces (with `containers.io`, `containers.iter`,
`containers.sexp`, `containers.data`) `containers.sexp`, `containers.data`)
=== other changes ### other changes
- cleanup: move sub-libraries to their own subdir each; mv everything into `src/` - cleanup: move sub-libraries to their own subdir each; mv everything into `src/`
- `sexp`: - `sexp`:
* `CCSexp` now splitted into `CCSexp` (manipulating expressions) and `CCSexpStream` * `CCSexp` now split into `CCSexp` (manipulating expressions) and `CCSexpStream`
* add `CCSexpM` for a simpler, monadic parser of S-expressions (deprecating `CCSexpStream`) * add `CCSexpM` for a simpler, monadic parser of S-expressions (deprecating `CCSexpStream`)
- `core`: - `core`:
* `CCString.fold` * `CCString.fold`
@ -626,7 +1181,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
* bugfix in `CCIO.read_all` and `CCIO.read_chunks` * bugfix in `CCIO.read_all` and `CCIO.read_chunks`
- use `-no-alias-deps` - use `-no-alias-deps`
== 0.6.1 ## 0.6.1
- use subtree `gen/` for `CCGen` (symlink) rather than a copy. - use subtree `gen/` for `CCGen` (symlink) rather than a copy.
- Add benchmarks for the function `iter` of iterators. - Add benchmarks for the function `iter` of iterators.
@ -634,14 +1189,14 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- `CCOpt.get_lazy` convenience function - `CCOpt.get_lazy` convenience function
- introduce `CCFloat`, add float functions to `CCRandom` (thanks to @struktured) - introduce `CCFloat`, add float functions to `CCRandom` (thanks to @struktured)
== 0.6 ## 0.6
=== breaking changes ### breaking changes
- new `CCIO` module, much simpler, but incompatible interface - new `CCIO` module, much simpler, but incompatible interface
- renamed `CCIO` to `advanced.CCMonadIO` - renamed `CCIO` to `advanced.CCMonadIO`
=== other changes ### other changes
- `CCMultiSet.{add_mult,remove_mult,update}` - `CCMultiSet.{add_mult,remove_mult,update}`
- `CCVector.{top,top_exn}` - `CCVector.{top,top_exn}`
@ -659,9 +1214,9 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
are now tailrec are now tailrec
== 0.5 ## 0.5
=== breaking changes ### breaking changes
- dependency on `cppo` (thanks to @whitequark, see `AUTHORS.md`) and `bytes` - dependency on `cppo` (thanks to @whitequark, see `AUTHORS.md`) and `bytes`
- `CCError`: - `CCError`:
@ -670,7 +1225,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- `CCPervasives.Opt` -> `CCPervasives.Option` - `CCPervasives.Opt` -> `CCPervasives.Option`
- `Levenshtein.Index.remove` changed signature (useless param removed) - `Levenshtein.Index.remove` changed signature (useless param removed)
=== other changes ### other changes
- stronger inlining for `CCVector` (so that e.g. push is inline) - stronger inlining for `CCVector` (so that e.g. push is inline)
- more tests for `CCVector` - more tests for `CCVector`
@ -685,7 +1240,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- add Format printers to `CCString` - add Format printers to `CCString`
- `AUTHORS.md` - `AUTHORS.md`
== 0.4.1 ## 0.4.1
- `CCOpt.get` - `CCOpt.get`
- new functions in `CCSexp.Traverse` - new functions in `CCSexp.Traverse`
@ -694,7 +1249,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- update of readme - update of readme
- generate doc for `containers.advanced` - generate doc for `containers.advanced`
== 0.4 ## 0.4
- `core/CCSexp` for fast and lightweight S-expressions parsing/printing - `core/CCSexp` for fast and lightweight S-expressions parsing/printing
- moved `CCLinq`, `CCBatch` and `CCat` from core/ to advanced/ - moved `CCLinq`, `CCBatch` and `CCat` from core/ to advanced/
@ -709,7 +1264,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- `CCPervasives` module, replacing modules of the standard library - `CCPervasives` module, replacing modules of the standard library
- removed type alias `CCString.t` (duplicate of String.t which already exists) - removed type alias `CCString.t` (duplicate of String.t which already exists)
== 0.3.4 ## 0.3.4
- subtree for `sequence` repo - subtree for `sequence` repo
- `CCSequence` is now a copy of `sequence` - `CCSequence` is now a copy of `sequence`
@ -719,7 +1274,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- specialize some comparison functions - specialize some comparison functions
- `CCOrd.map` - `CCOrd.map`
== 0.3.3 ## 0.3.3
- readme: add ci hook (to http://ci.cedeela.fr) - readme: add ci hook (to http://ci.cedeela.fr)
- `CCIO`: monad for IO actions-as-values - `CCIO`: monad for IO actions-as-values
@ -739,7 +1294,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- `CCString.init` - `CCString.init`
- `CCError.fail_printf` - `CCError.fail_printf`
== 0.3.2 ## 0.3.2
- small change in makefile - small change in makefile
- conversions for `CCString` - conversions for `CCString`
@ -764,7 +1319,7 @@ NOTE: use of `containers.io` is deprecated (its only module has moved to `contai
- `CCError.map2` - `CCError.map2`
- more combinators in `CCError` - more combinators in `CCError`
== 0.3.1 ## 0.3.1
- test for `CCArray.shuffle` - test for `CCArray.shuffle`
- bugfix in `CCArray.shuffle` - bugfix in `CCArray.shuffle`

9
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,9 @@
# Code of Conduct
This project has adopted the [OCaml Code of Conduct](https://github.com/ocaml/code-of-conduct/blob/main/CODE_OF_CONDUCT.md).
# Enforcement
This project follows the OCaml Code of Conduct
[enforcement policy](https://github.com/ocaml/code-of-conduct/blob/main/CODE_OF_CONDUCT.md#enforcement).
To report any violations, please contact @c-cube

View file

@ -1,32 +0,0 @@
= HOWTO
== Make a release
Beforehand, check `grep deprecated -r src` to see whether some functions
can be removed.
. `make test`
. update version in `_oasis`
. `make update_next_tag` (to update `@since` comments; be careful not to change symlinks)
. check status of modules (`{b status: foo}`) and update if required;
removed deprecated functions, etc.
. update `CHANGELOG.adoc` (see its end to find the right git command)
. commit the changes
. `git checkout stable`
. `git merge master`
. `oasis setup; make test doc`
. update `opam` (the version field; remove `oasis` in deps)
. tag, and push both to github
. `opam pin add containers https://github.com/c-cube/ocaml-containers.git#<release>`
. new opam package: `opam publish prepare; opam publish submit`
. re-generate doc: `make doc push_doc`
== List Authors
`git log --format='%aN' | sort -u`
== Subtree
If gen is https://github.com/c-cube/gen.git[this remote]:
git subtree pull --prefix gen gen master --squash

147
Makefile
View file

@ -1,145 +1,44 @@
# OASIS_START PACKAGES=containers,containers-data
# DO NOT EDIT (digest: 4c293511860bb966e727ba6f0ecc8197)
SETUP = ./setup.exe all: build test
build: setup.data $(SETUP) build:
$(SETUP) -build $(BUILDFLAGS) dune build @install -p $(PACKAGES)
doc: setup.data $(SETUP) build test: build
$(SETUP) -doc $(DOCFLAGS) # run tests in release mode to expose bug in #454
dune runtest --display=quiet --cache=disabled --no-buffer --force --profile=release
test: setup.data $(SETUP) build clean:
$(SETUP) -test $(TESTFLAGS) dune clean
all: $(SETUP) doc:
$(SETUP) -all $(ALLFLAGS) dune build @doc
install: setup.data $(SETUP) examples:
$(SETUP) -install $(INSTALLFLAGS) dune build examples/id_sexp.exe
uninstall: setup.data $(SETUP) format:
$(SETUP) -uninstall $(UNINSTALLFLAGS) @dune build $(DUNE_OPTS) @fmt --auto-promote
reinstall: setup.data $(SETUP) format-check:
$(SETUP) -reinstall $(REINSTALLFLAGS) @dune build $(DUNE_OPTS) @fmt --display=quiet
clean: $(SETUP)
$(SETUP) -clean $(CLEANFLAGS)
distclean: $(SETUP) VERSION=$(shell awk '/^version:/ {print $$2}' containers.opam)
$(SETUP) -distclean $(DISTCLEANFLAGS)
$(RM) $(SETUP)
setup.data: $(SETUP)
$(SETUP) -configure $(CONFIGUREFLAGS)
configure: $(SETUP)
$(SETUP) -configure $(CONFIGUREFLAGS)
setup.exe: setup.ml _oasis
ocamlfind ocamlopt -o $@ -linkpkg -package oasis.dynrun setup.ml || ocamlfind ocamlc -o $@ -linkpkg -package oasis.dynrun setup.ml || true
$(RM) setup.cmi setup.cmo setup.cmx setup.o
.PHONY: build doc test all install uninstall reinstall clean distclean configure
# OASIS_STOP
EXAMPLES = examples/mem_size.native examples/collatz.native \
examples/bencode_write.native # examples/crawl.native
OPTIONS = -use-ocamlfind -I _build
examples: all
ocamlbuild $(OPTIONS) -package unix -I . $(EXAMPLES)
push_doc: doc
rsync -tavu containers.docdir/* cedeela.fr:~/simon/root/software/containers/
push_doc_gh: doc
git checkout gh-pages && \
rm -rf dev/ && \
mkdir -p dev && \
cp -r containers.docdir/* dev/ && \
git add --all dev
DONTTEST=myocamlbuild.ml setup.ml $(wildcard src/**/*.cppo.*) $(wildcard src/**/*Labels*)
QTESTABLE=$(filter-out $(DONTTEST), \
$(wildcard src/core/*.ml) \
$(wildcard src/core/*.mli) \
$(wildcard src/data/*.ml) \
$(wildcard src/data/*.mli) \
$(wildcard src/string/*.ml) \
$(wildcard src/string/*.mli) \
$(wildcard src/unix/*.ml) \
$(wildcard src/unix/*.mli) \
$(wildcard src/sexp/*.ml) \
$(wildcard src/sexp/*.mli) \
$(wildcard src/iter/*.ml) \
$(wildcard src/iter/*.mli) \
$(wildcard src/bigarray/*.ml) \
$(wildcard src/bigarray/*.mli) \
$(wildcard src/threads/*.ml) \
$(wildcard src/threads/*.mli) \
)
qtest-clean:
@rm -rf qtest/
QTEST_PREAMBLE='open CCFun;; '
#qtest-build: qtest-clean build
# @mkdir -p qtest
# @qtest extract --preamble $(QTEST_PREAMBLE) \
# -o qtest/qtest_all.ml \
# $(QTESTABLE) 2> /dev/null
# @ocamlbuild $(OPTIONS) -pkg oUnit,QTest2Lib,ocamlbuildlib \
# -I core -I misc -I string \
# qtest/qtest_all.native
qtest-gen:
@mkdir -p qtest
@if which qtest > /dev/null ; then \
qtest extract --preamble $(QTEST_PREAMBLE) \
-o qtest/run_qtest.ml \
$(QTESTABLE) 2> /dev/null ; \
else touch qtest/run_qtest.ml ; \
fi
push-stable:
git checkout stable
git merge master -m 'merge from master'
oasis setup
git commit -a -m 'oasis files'
git push origin
git checkout master
clean-generated:
rm **/*.{mldylib,mlpack,mllib} myocamlbuild.ml -f
tags:
otags *.ml *.mli
VERSION=$(shell awk '/^Version:/ {print $$2}' _oasis)
update_next_tag: update_next_tag:
@echo "update version to $(VERSION)..." @echo "update version to $(VERSION)..."
zsh -c 'sed -i "s/NEXT_VERSION/$(VERSION)/g" **/*.ml **/*.mli' sed -i "s/NEXT_VERSION/$(VERSION)/g" $(wildcard src/**/*.ml) $(wildcard src/**/*.mli)
zsh -c 'sed -i "s/NEXT_RELEASE/$(VERSION)/g" **/*.ml **/*.mli' sed -i "s/NEXT_RELEASE/$(VERSION)/g" $(wildcard src/**/*.ml) $(wildcard src/**/*.mli)
devel:
./configure --enable-bench --enable-tests --enable-unix \
--enable-thread
make all
WATCH?=@src/check @tests/runtest
watch: watch:
while find src/ benchs/ -print0 | xargs -0 inotifywait -e delete_self -e modify ; do \ @dune build $(WATCH) -w
echo "============ at `date` ==========" ; \
make all; \
done
reindent: reindent:
@which ocp-indent || ( echo "require ocp-indent" ; exit 1 ) @which ocp-indent || ( echo "require ocp-indent" ; exit 1 )
@find src '(' -name '*.ml' -or -name '*.mli' ')' -type f -print0 | xargs -0 echo "reindenting: " @find src '(' -name '*.ml' -or -name '*.mli' ')' -type f -print0 | xargs -0 echo "reindenting: "
@find src '(' -name '*.ml' -or -name '*.mli' ')' -type f -print0 | xargs -0 ocp-indent -i @find src '(' -name '*.ml' -or -name '*.mli' ')' -type f -print0 | xargs -0 ocp-indent -i
.PHONY: examples push_doc tags qtest-gen qtest-clean devel update_next_tag .PHONY: all test clean build doc update_next_tag watch examples

View file

@ -1,478 +0,0 @@
= OCaml-containers =
:toc: macro
:source-highlighter: pygments
A modular, clean and powerful extension of the OCaml standard library.
https://c-cube.github.io/ocaml-containers/last/[(Jump to the current API documentation)].
Containers is an extension of OCaml's standard library (under BSD license)
focused on data structures, combinators and iterators, without dependencies on
unix, str or num. Every module is independent and is prefixed with 'CC' in the
global namespace. Some modules extend the stdlib (e.g. CCList provides safe
map/fold_right/append, and additional functions on lists).
Alternatively, `open Containers` will bring enhanced versions of the standard
modules into scope.
image::https://travis-ci.org/c-cube/ocaml-containers.svg?branch=master[alt="Build Status", link="https://travis-ci.org/c-cube/ocaml-containers"]
toc::[]
== Quick Summary
Containers is:
- A usable, reasonably well-designed library that extends OCaml's standard
library (in 'src/core/', packaged under `containers` in ocamlfind. Modules
are totally independent and are prefixed with `CC` (for "containers-core"
or "companion-cube" because I'm a megalomaniac). This part should be
usable and should work. For instance, `CCList` contains functions and
lists including safe versions of `map` and `append`. It also
provides a drop-in replacement to the standard library, in the module
`Containers` (intended to be opened, replaces some stdlib modules
with extended ones).
- Several small additional libraries that complement it:
* `containers.data` with additional data structures that don't have an
equivalent in the standard library;
* `containers.iter` with list-like and tree-like iterators;
- Utilities around the `unix` library in `containers.unix` (mainly to spawn
sub-processes easily and deal with resources safely)
- A lightweight S-expression printer and streaming parser in `containers.sexp`
- A library for threaded programming in `containers.thread`,
including a blocking queue, semaphores, an extension of `Mutex`, and
thread-pool based futures.
Some of the modules have been moved to their own repository (e.g. `sequence`,
`gen`, `qcheck`) and are on opam for great fun and profit.
== Change Log
See link:CHANGELOG.adoc[this file].
== Finding help
- http://lists.ocaml.org/listinfo/containers-users[Mailing List]
the address is mailto:containers-users@lists.ocaml.org[]
- the https://github.com/c-cube/ocaml-containers/wiki[github wiki]
- on IRC, ask `companion_cube` on `#ocaml@freenode.net`
- image:https://badges.gitter.im/Join%20Chat.svg[alt="Gitter", link="https://gitter.im/c-cube/ocaml-containers?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge"]
== Use
You might start with the <<tutorial>> to get a picture of how to use the library.
You can either build and install the library (see <<build>>), or just copy
files to your own project. The last solution has the benefits that you
don't have additional dependencies nor build complications (and it may enable
more inlining). Since modules have a friendly license and are mostly
independent, both options are easy.
In a toplevel, using ocamlfind:
[source,OCaml]
----
# #use "topfind";;
# #require "containers";;
# CCList.flat_map;;
- : ('a -> 'b list) -> 'a list -> 'b list = <fun>
# open Containers;; (* optional *)
# List.flat_map ;;
- : ('a -> 'b list) -> 'a list -> 'b list = <fun>
----
If you have comments, requests, or bugfixes, please share them! :-)
== License
This code is free, under the BSD license.
== Contents
See http://c-cube.github.io/ocaml-containers/[the documentation]
and <<tutorial,the tutorial below>> for a gentle introduction.
== Documentation
In general, see http://c-cube.github.io/ocaml-containers/last/ or
http://cedeela.fr/~simon/software/containers for the **API documentation**.
Some examples can be found link:doc/containers.adoc[there],
per-version doc http://c-cube.github.io/ocaml-containers/[there].
[[build]]
== Build
You will need OCaml `>=` 4.01.0.
=== Via opam
The prefered way to install is through http://opam.ocaml.org/[opam].
$ opam install containers
=== From Sources
On the branch `master` you will need `oasis` to build the library. On the
branch `stable` it is not necessary.
$ make
To build and run tests (requires `oUnit` and https://github.com/vincent-hugot/iTeML[qtest]):
$ opam install oUnit qtest
$ ./configure --enable-tests --enable-unix
$ make test
To build the small benchmarking suite (requires https://github.com/chris00/ocaml-benchmark[benchmark]):
$ opam install benchmark
$ make bench
$ ./benchs.native
== Contributing
PRs on github are very welcome (patches by email too, if you prefer so).
[[first-time-contribute]]
=== First-Time Contributors
Assuming your are in a clone of the repository:
. Some dependencies are required, you'll need
`opam install benchmark qcheck qtest sequence`.
. run `make devel` to enable everything (including tests).
. make your changes, commit, push, and open a PR.
. use `make test` without moderation! It must pass before a PR
is merged. There are around 900 tests right now, and new
features should come with their own tests.
If you feel like writing new tests, that is totally worth a PR
(and my gratefulness).
=== General Guidelines
A few guidelines to follow the philosophy of containers:
- no dependencies between basic modules (even just for signatures);
- add `@since` tags for new functions;
- add tests if possible (using https://github.com/vincent-hugot/iTeML/[qtest]). There are numerous inline tests already,
to see what it looks like search for comments starting with `(*$`
in source files.
=== For Total Beginners
Thanks for wanting to contribute!
To contribute a change, here are the steps (roughly):
. click "fork" on https://github.com/c-cube/ocaml-containers on the top right of the page. This will create a copy of the repository on your own github account.
. click the big green "clone or download" button, with "SSH". Copy the URL (which should look like `git@github.com:<your username>/ocaml-containers.git`) into a terminal to enter the command:
+
[source,sh]
----
$ git clone git@github.com:<your username>/ocaml-containers.git
----
+
. then, `cd` into the newly created directory.
. make the changes you want. See <<first-time-contribute>> for
more details about what to do in particular.
. use `git add` and `git commit` to commit these changes.
. `git push origin master` to push the new change(s) onto your
copy of the repository
. on github, open a "pull request" (PR). Et voilà !
[[tutorial]]
== Tutorial
This tutorial contains a few examples to illustrate the features and
usage of containers. We assume containers is installed and that
the library is loaded, e.g. with:
[source,OCaml]
----
#require "containers";;
----
=== Basics
We will start with a few list helpers, then look at other parts of
the library, including printers, maps, etc.
[source,OCaml]
----
(* quick reminder of this awesome standard operator *)
# (|>) ;;
- : 'a -> ('a -> 'b) -> 'b = <fun>
# open CCList.Infix;;
# let l = 1 -- 100;;
val l : int list = [1; 2; .....]
# l
|> CCList.filter_map
(fun x-> if x mod 3=0 then Some (float x) else None)
|> CCList.take 5 ;;
- : float list = [3.; 6.; 9.; 12.; 15.]
# let l2 = l |> CCList.take_while (fun x -> x<10) ;;
val l2 : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9]
(* an extension of Map.Make, compatible with Map.Make(CCInt) *)
# module IntMap = CCMap.Make(CCInt);;
(* conversions using the "sequence" type, fast iterators that are
pervasively used in containers. Combinators can be found
in the opam library "sequence". *)
# let map =
l2
|> List.map (fun x -> x, string_of_int x)
|> CCList.to_seq
|> IntMap.of_seq;;
val map : string CCIntMap.t = <abstr>
(* check the type *)
# CCList.to_seq ;;
- : 'a list -> 'a sequence = <fun>
# IntMap.of_seq ;;
- : (int * 'a) CCMap.sequence -> 'a IntMap.t = <fun>
(* we can print, too *)
# Format.printf "@[<2>map =@ @[<hov>%a@]@]@."
(IntMap.print CCFormat.int CCFormat.string_quoted)
map;;
map =
[1 --> "1", 2 --> "2", 3 --> "3", 4 --> "4", 5 --> "5", 6 --> "6",
7 --> "7", 8 --> "8", 9 --> "9"]
- : unit = ()
(* options are good *)
# IntMap.get 3 map |> CCOpt.map (fun s->s ^ s);;
- : string option = Some "33"
----
=== New types: `CCVector`, `CCHeap`, `CCResult`
Containers also contains (!) a few datatypes that are not from the standard
library but that are useful in a lot of situations:
CCVector::
A resizable array, with a mutability parameter. A value of type
`('a, CCVector.ro) CCVector.t` is an immutable vector of values of type `'a`,
whereas a `('a, CCVector.rw) CCVector.t` is a mutable vector that
can be modified. This way, vectors can be used in a quite functional
way, using operations such as `map` or `flat_map`, or in a more
imperative way.
CCHeap::
A priority queue (currently, leftist heaps) functorized over
a module `sig val t val leq : t -> t -> bool` that provides a type `t`
and a partial order `leq` on `t`.
CCResult::
An error type for making error handling more explicit (an error monad,
really, if you're not afraid of the "M"-word).
Subsumes and replaces the old `CCError`.
It uses the new `result` type from the standard library (or from
the retrocompatibility package on opam) and provides
many combinators for dealing with `result`.
Now for a few examples:
[source,OCaml]
----
(* create a new empty vector. It is mutable, for otherwise it would
not be very useful. *)
# CCVector.create;;
- : unit -> ('a, CCVector.rw) CCVector.t = <fun>
(* init, similar to Array.init, can be used to produce a
vector that is mutable OR immutable (see the 'mut parameter?) *)
# CCVector.init ;;
- : int -> (int -> 'a) -> ('a, 'mut) CCVector.t = <fun>c
(* use the infix (--) operator for creating a range. Notice
that v is a vector of integer but its mutability is not
decided yet. *)
# let v = CCVector.(1 -- 10);;
val v : (int, '_a) CCVector.t = <abstr>
# Format.printf "v = @[%a@]@." (CCVector.print CCInt.print) v;;
v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(* now let's mutate v *)
# CCVector.push v 42;;
- : unit = ()
(* now v is a mutable vector *)
# v;;
- : (int, CCVector.rw) CCVector.t = <abstr>
(* functional combinators! *)
# let v2 = v
|> CCVector.map (fun x-> x+1)
|> CCVector.filter (fun x-> x mod 2=0)
|> CCVector.rev ;;
val v2 : (int, '_a) CCVector.t = <abstr>
# Format.printf "v2 = @[%a@]@." (CCVector.print CCInt.print) v2;;
v2 = [10, 8, 6, 4, 2]
(* let's transfer to a heap *)
# module IntHeap = CCHeap.Make(struct type t = int let leq = (<=) end);;
# let h = v2 |> CCVector.to_seq |> IntHeap.of_seq ;;
val h : IntHeap.t = <abstr>
(* We can print the content of h
(printing is not necessarily in order, though) *)
# Format.printf "h = [@[%a@]]@." (IntHeap.print CCInt.print) h;;
h = [2,4,6,8,10]
(* we can remove the first element, which also returns a new heap
that does not contain it — CCHeap is a functional data structure *)
# IntHeap.take h;;
- : (IntHeap.t * int) option = Some (<abstr>, 2)
# let h', x = IntHeap.take_exn h ;;
val h' : IntHeap.t = <abstr>
val x : int = 2
(* see, 2 is removed *)
# IntHeap.to_list h' ;;
- : int list = [4; 6; 8; 10]
----
=== IO helpers
The core library contains a module called `CCIO` that provides useful
functions for reading and writing files. It provides functions that
make resource handling easy, following
the pattern `with_resource : resource -> (access -> 'a) -> 'a` where
the type `access` is a temporary handle to the resource (e.g.,
imagine `resource` is a file name and `access` a file descriptor).
Calling `with_resource r f` will access `r`, give the result to `f`,
compute the result of `f` and, whether `f` succeeds or raises an
error, it will free the resource.
Consider for instance:
[source,OCaml]
----
# CCIO.with_out "/tmp/foobar"
(fun out_channel ->
CCIO.write_lines_l out_channel ["hello"; "world"]);;
- : unit = ()
----
This just opened the file '/tmp/foobar', creating it if it didn't exist,
and wrote two lines in it. We did not have to close the file descriptor
because `with_out` took care of it. By the way, the type signatures are:
[source,OCaml]
----
val with_out :
?mode:int -> ?flags:open_flag list ->
string -> (out_channel -> 'a) -> 'a
val write_lines_l : out_channel -> string list -> unit
----
So we see the pattern for `with_out` (which opens a function in write
mode and gives its functional argument the corresponding file descriptor).
NOTE: you should never let the resource escape the
scope of the `with_resource` call, because it will not be valid outside.
OCaml's type system doesn't make it easy to forbid that so we rely
on convention here (it would be possible, but cumbersome, using
a record with an explicitely quantified function type).
Now we can read the file again:
[source,OCaml]
----
# let lines = CCIO.with_in "/tmp/foobar" CCIO.read_lines_l ;;
val lines : string list = ["hello"; "world"]
----
There are some other functions in `CCIO` that return _generators_
instead of lists. The type of generators in containers
is `type 'a gen = unit -> 'a option` (combinators can be
found in the opam library called "gen"). A generator is to be called
to obtain successive values, until it returns `None` (which means it
has been exhausted). In particular, python users might recognize
the function
[source,OCaml]
----
# CCIO.File.walk ;;
- : string -> walk_item gen = <fun>;;
----
where `type walk_item = [ `Dir | `File ] * string` is a path
paired with a flag distinguishing files from directories.
=== To go further: containers.data
There is also a sub-library called `containers.data`, with lots of
more specialized data-structures.
The documentation contains the API for all the modules
(see link:README.adoc[the readme]); they also provide
interface to `sequence` and, as the rest of containers, minimize
dependencies over other modules. To use `containers.data` you need to link it,
either in your build system or by `#require containers.data;;`
A quick example based on purely functional double-ended queues:
[source,OCaml]
----
# #require "containers.data";;
# #install_printer CCFQueue.print;; (* better printing of queues! *)
# let q = CCFQueue.of_list [2;3;4] ;;
val q : int CCFQueue.t = queue {2; 3; 4}
# let q2 = q |> CCFQueue.cons 1 |> CCFQueue.cons 0 ;;
val q2 : int CCFQueue.t = queue {0; 1; 2; 3; 4}
(* remove first element *)
# CCFQueue.take_front q2;;
- : (int * int CCFQueue.t) option = Some (0, queue {1; 2; 3; 4})
(* q was not changed *)
# CCFQueue.take_front q;;
- : (int * int CCFQueue.t) option = Some (2, queue {3; 4})
(* take works on both ends of the queue *)
# CCFQueue.take_back_l 2 q2;;
- : int CCFQueue.t * int list = (queue {0; 1; 2}, [3; 4])
----
=== Common Type Definitions
Some structural types are used throughout the library:
gen:: `'a gen = unit -> 'a option` is an iterator type. Many combinators
are defined in the opam library https://github.com/c-cube/gen[gen]
sequence:: `'a sequence = (unit -> 'a) -> unit` is also an iterator type.
It is easier to define on data structures than `gen`, but it a bit less
powerful. The opam library https://github.com/c-cube/sequence[sequence]
can be used to consume and produce values of this type.
error:: `'a or_error = ('a, string) result = Error of string | Ok of 'a`
using the standard `result` type, supported in `CCResult`.
klist:: `'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]` is a lazy list
without memoization, used as a persistent iterator. The reference
module is `CCKList` (in `containers.iter`).
printer:: `'a printer = Format.formatter -> 'a -> unit` is a pretty-printer
to be used with the standard module `Format`. In particular, in many cases,
`"foo: %a" Foo.print foo` will type-check.
=== Extended Documentation
See link:doc/containers.adoc[the extended documentation] for more examples.
Powered by image:http://oasis.forge.ocamlcore.org/oasis-badge.png[alt="OASIS", style="border: none;", link="http://oasis.forge.ocamlcore.org/"]

699
README.md Normal file
View file

@ -0,0 +1,699 @@
# OCaml-containers 📦 [![Build and test](https://github.com/c-cube/ocaml-containers/actions/workflows/main.yml/badge.svg)](https://github.com/c-cube/ocaml-containers/actions/workflows/main.yml)
A modular, clean and powerful extension of the OCaml standard library.
[(Jump to the current API documentation)](https://c-cube.github.io/ocaml-containers/)
Containers is an extension of OCaml's standard library (under BSD license)
focused on data structures, combinators and iterators, without dependencies on
unix, str or num. Every module is independent and is prefixed with 'CC' in the
global namespace. Some modules extend the stdlib (e.g. `CCList` provides safe
`map`/`fold_right`/`append`, and additional functions on lists).
Alternatively, `open Containers` will bring enhanced versions of the standard
modules into scope.
## Quick Summary
Containers is:
- A usable, reasonably well-designed library that extends OCaml's standard
library (in 'src/core/', packaged under `containers` in ocamlfind. Modules
are totally independent and are prefixed with `CC` (for "containers-core"
or "companion-cube" because I'm a megalomaniac). This part should be
usable and should work. For instance, `CCList` contains functions and
lists including safe versions of `map` and `append`. It also
provides a drop-in replacement to the standard library, in the module
`Containers` (intended to be opened, replaces some stdlib modules
with extended ones), and a small S-expression printer and parser
that can be functorized over the representation of values.
- Some sub-libraries with a specific focus each:
* Utilities around the `unix` library in `containers.unix` (mainly to spawn
sub-processes easily and deal with resources safely)
* A bencode codec in `containers.bencode`. This is a tiny json-like
serialization format that is extremely simple. It comes from bittorrent files.
* A [CBOR](https://cbor.io) codec in `containers.cbor`. This is a
compact binary serialization format.
* The [Strongly Connected Component](https://en.wikipedia.org/wiki/Strongly_connected_component)
algorithm, functorized, in `containers.scc`
- A separate library `containers-data` with additional
data structures that don't have an equivalent in the standard library,
typically not as thoroughly maintained. This is now in its own package
since 3.0.
Some of the modules have been moved to their own repository (e.g. `sequence` (now `iter`),
`gen`, `qcheck`) and are on opam for great fun and profit.
Containers-thread has been removed in favor of [Moonpool](https://github.com/c-cube/moonpool/).
## Migration Guide
### To 3.0
The [changelog's breaking section](CHANGELOG.md) contains a list of the breaking
changes in this release.
1. The biggest change is that some sub-libraries have been either turned into
their own packages (`containers-data`),
deleted (`containers.iter`),or merged elsewhere (`containers.sexp`).
This means that if use these libraries you will have to edit your
`dune`/`_oasis`/`opam` files.
- if you use `containers.sexp` (i.e. the `CCSexp` module), it now lives in
`containers` itself.
- if you used anything in `containers.data`, you need to depend on the
`containers-data` package now.
2. Another large change is the removal (at last!) of functions deprecated
in 2.8, related to the spread of `Seq.t` as the standard iterator type.
Functions like `CCVector.of_seq` now operate on this standard `Seq.t` type,
and old-time iteration based on [iter](https://github.com/c-cube/iter)
is now named `of_iter`, `to_iter`, etc.
Here you need to change your code, possibly using search and replace.
Thankfully, the typechecker should guide you.
3. `Array_slice` and `String.Sub` have been removed to simplify the
code and `String` more lightweight. There is no replacement at the moment.
Please tell us if you need this to be turned into a sub-library.
4. Renaming of some functions into more explicit/clear names.
Examples:
* `CCVector.shrink` is now `CCVector.truncate`
* `CCVector.remove` is now `CCVector.remove_unordered`, to be
contrasted with the new `CCVector.remove_and_shift`.
* `CCPair.map_fst` and `map_snd` now transform a tuple into another tuple
by modify the first (resp. second) element.
5. All the collection pretty-printers now take their separator/start/stop
optional arguments as `unit printer` (i.e. `Format.formatter -> unit -> unit`
functions) rather than strings. This gives the caller better control
over the formatting of lists, arrays, queues, tables, etc.
6. Removal of many deprecated functions.
### To 2.0
- The type system should detect issues related to `print` renamed into `pp` easily.
If you are lucky, a call to `sed -i 's/print/pp/g'` on the concerned files
might help rename all the calls
properly.
- many optional arguments have become mandatory, because their default value
would be a polymorphic "magic" operator such as `(=)` or `(>=)`.
Now these have to be specified explicitly, but during the transition
you can use `Stdlib.(=)` and `Stdlib.(>=)` as explicit arguments.
- if your code contains `open Containers`, the biggest hurdle you face
might be that operators have become monomorphic by default.
We believe this is a useful change that prevents many subtle bugs.
However, during migration and until you use proper combinators for
equality (`CCEqual`), comparison (`CCOrd`), and hashing (`CCHash`),
you might want to add `open Stdlib` just after the `open Containers`.
See [the section on monomorphic operators](#monomorphic-operators-why-and-how) for more details.
## Monomorphic operators: why, and how?
### Why shadow polymorphic operators by default?
To quote @bluddy in [#196](https://github.com/c-cube/ocaml-containers/issues/196):
The main problem with polymorphic comparison is that many data structures will
give one result for structural comparison, and a different result for semantic
comparison. The classic example is comparing maps. If you have a list of maps
and try to use comparison to sort them, you'll get the wrong result: multiple
map structures can represent the same semantic mapping from key to value, and
comparing them in terms of structure is simply wrong. A far more pernicious bug
occurs with hashtables. Identical hashtables will seem to be identical for a
while, as before they've had a key clash, the outer array is likely to be the
same. Once you get a key clash though, you start getting lists inside the
arrays (or maps inside the arrays if you try to make a smarter hashtable) and
that will cause comparison errors ie. identical hashtables will be seen as
different or vice versa.
Every time you use a polymorphic comparison where you're using a data type
where structural comparison != semantic comparison, it's a bug. And every time
you use polymorphic comparison where the type of data being compared may vary
(e.g. it's an int now, but it may be a map later), you're planting a bug for
the future.
See also:
- https://blog.janestreet.com/the-perils-of-polymorphic-compare/
- https://blog.janestreet.com/building-a-better-compare/
### Sometimes polymorphic operators still make sense!
If you just want to use polymorphic operators, it's fine! You can access them
easily by using `Stdlib.(=)`, `Stdlib.max`, etc.
When migrating a module, you can add `open Stdlib` on top of it to restore
the default behavior. It is, however, recommended to export an `equal` function
(and `compare`, and `hash`) for all the public types, even if their internal
definition is just the corresponding polymorphic operator.
This way, other modules can refer to `Foo.equal` and will not have to be
updated the day `Foo.equal` is no longer just polymorphic equality.
Another bonus is that `Hashtbl.Make(Foo)` or `Map.Make(Foo)` will just work™.
### Further discussions
See issues
[#196](https://github.com/c-cube/ocaml-containers/issues/196),
[#197](https://github.com/c-cube/ocaml-containers/issues/197)
## Debugging with `ocamldebug`
To print values with types defined in `containers` in the bytecode debugger,
you first have to load the appropriate bytecode archives. After starting a
session, e.g. `ocamldebug your_program.bc`,
```ocaml non-deterministic=command
# #load_printer containers_monomorphic.cma;;
# #load_printer containers.cma;;
```
For these archives to be found, you may have to `run` the program first. Now
printing functions that have the appropriate type `Format.formatter -> 'a ->
unit` can be installed. For example,
```ocaml non-deterministic=command
# #install_printer Containers.Int.pp;;
```
However, printer combinators are not easily handled by `ocamldebug`. For
instance `# install_printer Containers.(List.pp Int.pp)` will *not* work out of
the box. You can make this work by writing a short module which defines
ready-made combined printing functions, and loading that in ocamldebug. For
instance
```ocaml non-deterministic=command
module M = struct
let pp_int_list = Containers.(List.pp Int.pp)
end;;
```
loaded via `# load_printer m.cmo` and installed as `# install_printer
M.pp_int_list`.
## Change Log
See [this file](./CHANGELOG.md).
## Finding help
- [Mailing List](http://lists.ocaml.org/listinfo/containers-users)
the address is <mailto:containers-users@lists.ocaml.org>
- the [github wiki](https://github.com/c-cube/ocaml-containers/wiki)
- on IRC, ask `companion_cube` on `#ocaml@irc.libera.chat`
- there is a `#containers` channel on OCaml's discord server.
## Use
You might start with the [tutorial](#tutorial) to get a picture of how to use the library.
You can either build and install the library (see [build](#build)), or just copy
files to your own project. The last solution has the benefits that you
don't have additional dependencies nor build complications (and it may enable
more inlining). Since modules have a friendly license and are mostly
independent, both options are easy.
In a toplevel, using ocamlfind:
```ocaml
# #use "topfind";;
...
# #require "containers";;
# #require "containers-data";;
# CCList.flat_map;;
- : ('a -> 'b list) -> 'a list -> 'b list = <fun>
# open Containers (* optional *);;
# List.flat_map ;;
- : ('a -> 'b list) -> 'a list -> 'b list = <fun>
```
If you have comments, requests, or bugfixes, please share them! :-)
## License
This code is free, under the BSD license.
## Contents
See [the documentation](http://c-cube.github.io/ocaml-containers/)
and [the tutorial below](#tutorial) for a gentle introduction.
## Documentation
In general, see http://c-cube.github.io/ocaml-containers/last/ for the **API documentation**.
Some examples can be found [there](doc/containers.md),
per-version doc [there](http://c-cube.github.io/ocaml-containers/).
## Build
You will need OCaml `>=` 4.03.0.
### Via opam
The preferred way to install is through [opam](http://opam.ocaml.org/).
```
$ opam install containers
```
### From Sources
<details>
You need dune (formerly jbuilder).
```
$ make
```
To build and run tests (requires `qcheck-core`, `gen`, `iter`):
```
$ opam install qcheck-core
$ make test
```
To build the small benchmarking suite (requires [benchmark](https://github.com/chris00/ocaml-benchmark)):
```
$ opam install benchmark batteries
$ make bench
$ ./benchs/run_benchs.sh
```
</details>
## Contributing
PRs on github are very welcome (patches by email too, if you prefer so).
<details>
<summary>how to contribute (click to unfold)</summary>
### List of authors
The list of contributors can be seen [on github](https://github.com/c-cube/ocaml-containers/graphs/contributors).
Alternatively, `git authors` from git-extras can be invoked from within the repo
to list authors based on the git commits.
### First-Time Contributors
Assuming your are in a clone of the repository:
1. Some dependencies are required, you'll need
`opam install benchmark qcheck-core iter gen mdx uutf yojson`.
2. run `make all` to enable everything (including tests).
3. make your changes, commit, push, and open a PR.
4. use `make test` without moderation! It must pass before a PR
is merged. There are around 1150 tests right now, and new
features should come with their own tests.
If you feel like writing new tests, that is totally worth a PR
(and my gratefulness).
### General Guidelines
A few guidelines to follow the philosophy of containers:
- no dependencies between basic modules (even just for signatures);
- add `@since` tags for new functions;
- add tests if possible (see `tests/` dir)
There are numerous inline tests already,
to see what it looks like search for comments starting with `(*$`
in source files.
### For Total Beginners
Thanks for wanting to contribute!
To contribute a change, here are the steps (roughly):
1. click "fork" on https://github.com/c-cube/ocaml-containers on the top right of the page. This will create a copy of the repository on your own github account.
2. click the big green "clone or download" button, with "SSH". Copy the URL (which should look like `git@github.com:<your username>/ocaml-containers.git`) into a terminal to enter the command:
```
$ git clone git@github.com:<your username>/ocaml-containers.git
```
3. then, `cd` into the newly created directory.
4. make the changes you want. See <#first-time-contributors> for
more details about what to do in particular.
5. use `git add` and `git commit` to commit these changes.
6. `git push origin master` to push the new change(s) onto your
copy of the repository
7. on github, open a "pull request" (PR). Et voilà !
</details>
## Tutorial
This tutorial contains a few examples to illustrate the features and
usage of containers.
<details>
<summary>an introduction to containers (click to unfold)</summary>
We assume containers is installed and that
the library is loaded, e.g. with:
```ocaml
# #require "containers";;
# Format.set_margin 50 (* for readability here *);;
- : unit = ()
```
### Basics
We will start with a few list helpers, then look at other parts of
the library, including printers, maps, etc.
```ocaml
# (|>) (* quick reminder of this awesome standard operator *);;
- : 'a -> ('a -> 'b) -> 'b = <fun>
# 10 |> succ;;
- : int = 11
# open CCList.Infix;;
# let l = 1 -- 100;;
val l : int list =
[1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21;
22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39;
40; 41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57;
58; 59; 60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75;
76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93;
94; 95; 96; 97; 98; 99; 100]
# (* transform a list, dropping some elements *)
l
|> CCList.filter_map
(fun x-> if x mod 3=0 then Some (float x) else None)
|> CCList.take 5 ;;
- : float list = [3.; 6.; 9.; 12.; 15.]
# let l2 = l |> CCList.take_while (fun x -> x<10) ;;
val l2 : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9]
```
```ocaml
(* an extension of Map.Make, compatible with Map.Make(CCInt) *)
module IntMap = CCMap.Make(CCInt);;
```
```ocaml
# (* conversions using the "iter" type, fast iterators that are
pervasively used in containers. Combinators can be found
in the opam library "iter". *)
let map : string IntMap.t =
l2
|> List.map (fun x -> x, string_of_int x)
|> CCList.to_iter
|> IntMap.of_iter;;
val map : string IntMap.t = <abstr>
# CCList.to_iter (* check the type *);;
- : 'a list -> 'a CCList.iter = <fun>
# IntMap.of_iter ;;
- : (int * 'a) CCMap.iter -> 'a IntMap.t = <fun>
# (* we can print, too *)
Format.printf "@[<2>map =@ @[<hov>%a@]@]@."
(IntMap.pp CCFormat.int CCFormat.string_quoted)
map;;
map =
1 -> "1", 2 -> "2", 3 -> "3", 4 -> "4", 5
-> "5", 6 -> "6", 7 -> "7", 8 -> "8", 9 -> "9"
- : unit = ()
# (* options are good *)
IntMap.get 3 map |> CCOption.map (fun s->s ^ s);;
- : string option = Some "33"
```
### New types: `CCVector`, `CCHeap`, `CCResult`, `CCSexp`, `CCByte_buffer`
Containers also contains (!) a few datatypes that are not from the standard
library but that are useful in a lot of situations:
- `CCVector`:
A resizable array, with a mutability parameter. A value of type
`('a, CCVector.ro) CCVector.t` is an immutable vector of values of type `'a`,
whereas a `('a, CCVector.rw) CCVector.t` is a mutable vector that
can be modified. This way, vectors can be used in a quite functional
way, using operations such as `map` or `flat_map`, or in a more
imperative way.
- `CCHeap`:
A priority queue (currently, leftist heaps) functorized over
a module `sig val t val leq : t -> t -> bool` that provides a type `t`
and a partial order `leq` on `t`.
- `CCResult`
An error type for making error handling more explicit (an error monad,
really, if you're not afraid of the "M"-word).
Subsumes and replaces the old `CCError`.
It uses the new `result` type from the standard library (or from
the retrocompatibility package on opam) and provides
many combinators for dealing with `result`.
- `CCSexp` and `CCCanonical_sexp`:
functorized printer and parser for S-expressions, respectively as
actual S-expressions (like `sexplib`) and as canonical binary-safe
S-expressions (like `csexp`)
- `CCByte_buffer`: a better version of the standard `Buffer.t` which cannot be
extended and prevents access to its internal byte array. This type is
designed for (blocking) IOs and to produce complex strings incrementally
in an efficient way.
Now for a few examples:
```ocaml
# (* create a new empty vector. It is mutable, for otherwise it would
not be very useful. *)
CCVector.create;;
- : unit -> ('a, CCVector.rw) CCVector.t = <fun>
# (* init, similar to Array.init, can be used to produce a
vector that is mutable OR immutable (see the 'mut parameter?) *)
CCVector.init ;;
- : int -> (int -> 'a) -> ('a, 'mut) CCVector.t = <fun>
```
```ocaml non-deterministic=output
# (* use the infix (--) operator for creating a range. Notice
that v is a vector of integer but its mutability is not
decided yet. *)
let v = CCVector.(1 -- 10);;
val v : (int, '_a) CCVector.t = <abstr>
```
```ocaml
# Format.printf "v = @[%a@]@." (CCVector.pp CCInt.pp) v;;
v = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
- : unit = ()
# CCVector.push v 42;;
- : unit = ()
# v (* now v is a mutable vector *);;
- : (int, CCVector.rw) CCVector.t = <abstr>
# (* functional combinators! *)
let v2 : _ CCVector.ro_vector = v
|> CCVector.map (fun x-> x+1)
|> CCVector.filter (fun x-> x mod 2=0)
|> CCVector.rev ;;
val v2 : int CCVector.ro_vector = <abstr>
# Format.printf "v2 = @[%a@]@." (CCVector.pp CCInt.pp) v2;;
v2 = 10, 8, 6, 4, 2
- : unit = ()
```
```ocaml
(* let's transfer to a heap *)
module IntHeap = CCHeap.Make(struct type t = int let leq = (<=) end);;
```
```ocaml
# let h = v2 |> CCVector.to_iter |> IntHeap.of_iter ;;
val h : IntHeap.t = <abstr>
# (* We can print the content of h
(printing is not necessarily in order, though) *)
Format.printf "h = [@[%a@]]@." (IntHeap.pp CCInt.pp) h;;
h = [2,4,6,8,10]
- : unit = ()
# (* we can remove the first element, which also returns a new heap
that does not contain it — CCHeap is a functional data structure *)
IntHeap.take h;;
- : (IntHeap.t * int) option = Some (<abstr>, 2)
# let h', x = IntHeap.take_exn h ;;
val h' : IntHeap.t = <abstr>
val x : int = 2
# IntHeap.to_list h' (* see, 2 is removed *);;
- : int list = [4; 8; 10; 6]
```
### IO helpers
The core library contains a module called `CCIO` that provides useful
functions for reading and writing files. It provides functions that
make resource handling easy, following
the pattern `with_resource : resource -> (access -> 'a) -> 'a` where
the type `access` is a temporary handle to the resource (e.g.,
imagine `resource` is a file name and `access` a file descriptor).
Calling `with_resource r f` will access `r`, give the result to `f`,
compute the result of `f` and, whether `f` succeeds or raises an
error, it will free the resource.
Consider for instance:
```ocaml
# CCIO.with_out "./foobar"
(fun out_channel ->
CCIO.write_lines_l out_channel ["hello"; "world"]);;
- : unit = ()
```
This just opened the file 'foobar', creating it if it didn't exist,
and wrote two lines in it. We did not have to close the file descriptor
because `with_out` took care of it. By the way, the type signatures are:
```ocaml non-deterministic=command
val with_out :
?mode:int -> ?flags:open_flag list ->
string -> (out_channel -> 'a) -> 'a
val write_lines_l : out_channel -> string list -> unit
```
So we see the pattern for `with_out` (which opens a function in write
mode and gives its functional argument the corresponding file descriptor).
NOTE: you should never let the resource escape the
scope of the `with_resource` call, because it will not be valid outside.
OCaml's type system doesn't make it easy to forbid that so we rely
on convention here (it would be possible, but cumbersome, using
a record with an explicitly quantified function type).
Now we can read the file again:
```ocaml
# let lines : string list = CCIO.with_in "./foobar" CCIO.read_lines_l ;;
val lines : string list = ["hello"; "world"]
```
There are some other functions in `CCIO` that return _generators_
instead of lists. The type of generators in containers
is `type 'a gen = unit -> 'a option` (combinators can be
found in the opam library called "gen"). A generator is to be called
to obtain successive values, until it returns `None` (which means it
has been exhausted). In particular, python users might recognize
the function
```ocaml non-deterministic=command
# CCIO.File.walk ;;
- : string -> walk_item gen = <fun>;;
```
where `type walk_item = [ ``Dir | ``File ] * string` is a path
paired with a flag distinguishing files from directories.
### To go further: `containers-data`
There is also a library called `containers-data`, with lots of
more specialized data-structures.
The documentation contains the API for all the modules; they also provide
interface to `iter` and, as the rest of containers, minimize
dependencies over other modules. To use `containers-data` you need to link it,
either in your build system or by `#require containers-data;;`
A quick example based on purely functional double-ended queues:
```ocaml
# #require "containers-data";;
# #install_printer CCFQueue.pp (* better printing of queues! *);;
# let q = CCFQueue.of_list [2;3;4] ;;
val q : int CCFQueue.t = queue {2; 3; 4}
# let q2 = q |> CCFQueue.cons 1 |> CCFQueue.cons 0 ;;
val q2 : int CCFQueue.t = queue {0; 1; 2; 3; 4}
# (* remove first element *)
CCFQueue.take_front q2;;
- : (int * int CCFQueue.t) option = Some (0, queue {1; 2; 3; 4})
# (* q was not changed *)
CCFQueue.take_front q;;
- : (int * int CCFQueue.t) option = Some (2, queue {3; 4})
# (* take works on both ends of the queue *)
CCFQueue.take_back_l 2 q2;;
- : int CCFQueue.t * int list = (queue {0; 1; 2}, [3; 4])
```
### Common Type Definitions
Some structural types are used throughout the library:
- `gen`: `'a gen = unit -> 'a option` is an iterator type. Many combinators
are defined in the opam library [gen](https://github.com/c-cube/gen)
- `iter`: `'a iter = (unit -> 'a) -> unit` is also an iterator type, formerly
named `sequence`.
It is easier to define on data structures than `gen`, but it a bit less
powerful. The opam library [iter](https://github.com/c-cube/iter)
can be used to consume and produce values of this type.
It was renamed
from `'a sequence` to `'a iter` to distinguish it better from `Core.Sequence`
and the standard `seq`.
- `error`: `'a or_error = ('a, string) result = Error of string | Ok of 'a`
using the standard `result` type, supported in `CCResult`.
- `printer`: `'a printer = Format.formatter -> 'a -> unit` is a pretty-printer
to be used with the standard module `Format`. In particular, in many cases,
`"foo: %a" Foo.print foo` will type-check.
### Extended Documentation
See [the extended documentation](doc/containers.md) for more examples.
</details>
## HOWTO (for contributors)
<details>
### Make a release
Beforehand, check `grep deprecated -r src` to see whether some functions
can be removed.
- `make all`
- update version in `containers.opam`
- `make update_next_tag` (to update `@since` comments; be careful not to change symlinks)
- check status of modules (`{b status: foo}`) and update if required;
removed deprecated functions, etc.
- update `CHANGELOG.md` (see its end to find the right git command)
- commit the changes
- `make test doc`
- `export VERSION=<tag here>; git tag -f $VERSION; git push origin :$VERSION; git push origin $VERSION`
- new opam package: `opam publish https://github.com/c-cube/ocaml-containers/archive/<tag>.tar.gz`
- re-generate doc: `make doc` and put it into `gh-pages`
### List Authors
```
git log --format='%aN' | sort -u
```
</details>

161
_oasis
View file

@ -1,161 +0,0 @@
OASISFormat: 0.4
Name: containers
Version: 1.5
Homepage: https://github.com/c-cube/ocaml-containers
Authors: Simon Cruanes
License: BSD-2-clause
LicenseFile: LICENSE
Plugins: META (0.3), DevFiles (0.3)
OCamlVersion: >= 4.00.1
BuildTools: ocamlbuild
AlphaFeatures: compiled_setup_ml, ocamlbuild_more_args
# cygwin fails with anything else
XOCamlbuildExtraArgs: "-j 1"
Synopsis: A modular standard library focused on data structures.
Description:
Containers is a standard library (BSD license) focused on data structures,
combinators and iterators, without dependencies on unix. Every module is
independent and is prefixed with 'CC' in the global namespace. Some modules
extend the stdlib (e.g. CCList provides safe map/fold_right/append, and
additional functions on lists).
It also features optional libraries for dealing with strings, and
helpers for unix and threads.
Flag "unix"
Description: Build the containers.unix library (depends on Unix)
Default: true
Flag "thread"
Description: Build modules that depend on threads
Default: true
Flag "bench"
Description: Build and run benchmarks
Default: true
Library "containers"
Path: src/core
Modules: CCVector, CCHeap, CCList, CCOpt, CCPair,
CCFun, CCHash, CCInt, CCBool, CCFloat, CCArray, CCRef, CCSet,
CCOrd, CCRandom, CCString, CCHashtbl, CCMap, CCFormat, CCIO,
CCInt64, CCChar, CCResult, CCParse, CCArray_slice,
CCListLabels, CCArrayLabels, CCEqual,
Containers
BuildDepends: bytes, result
# BuildDepends: bytes, bisect_ppx
Library "containers_unix"
Path: src/unix
Modules: CCUnix
BuildDepends: bytes, result, unix
FindlibParent: containers
FindlibName: unix
Library "containers_sexp"
Path: src/sexp
Modules: CCSexp, CCSexp_lex
BuildDepends: bytes, result
FindlibParent: containers
FindlibName: sexp
Library "containers_data"
Path: src/data
Modules: CCMultiMap, CCMultiSet, CCTrie, CCFlatHashtbl, CCCache,
CCPersistentHashtbl, CCDeque, CCFQueue, CCBV, CCMixtbl,
CCMixmap, CCRingBuffer, CCIntMap, CCPersistentArray,
CCMixset, CCGraph, CCHashSet, CCBitField,
CCHashTrie, CCWBTree, CCRAL, CCSimple_queue,
CCImmutArray, CCHet, CCZipper
BuildDepends: bytes
# BuildDepends: bytes, bisect_ppx
FindlibParent: containers
FindlibName: data
Library "containers_iter"
Path: src/iter
Modules: CCKTree, CCKList, CCLazy_list
FindlibParent: containers
FindlibName: iter
Library "containers_thread"
Path: src/threads/
Modules: CCPool, CCLock, CCSemaphore, CCThread, CCBlockingQueue,
CCTimer
FindlibName: thread
FindlibParent: containers
Build$: flag(thread)
Install$: flag(thread)
BuildDepends: containers, threads
XMETARequires: containers, threads
Library "containers_top"
Path: src/top/
Modules: Containers_top
FindlibName: top
FindlibParent: containers
BuildDepends: compiler-libs.common, containers, containers.data,
containers.unix, containers.sexp, containers.iter
Document containers
Title: Containers docs
Type: ocamlbuild (0.3)
BuildTools+: ocamldoc
Build$: flag(docs) && flag(unix)
Install: true
XOCamlbuildPath: .
XOCamlbuildExtraArgs:
"-docflags '-colorize-code -short-functors -charset utf-8'"
XOCamlbuildLibraries:
containers, containers.iter, containers.data,
containers.thread, containers.unix, containers.sexp
Executable run_benchs
Path: benchs/
Install: false
CompiledObject: best
Build$: flag(bench)
MainIs: run_benchs.ml
BuildDepends: containers, qcheck,
containers.data, containers.iter, containers.thread,
sequence, gen, benchmark
Executable run_bench_hash
Path: benchs/
Install: false
CompiledObject: best
Build$: flag(bench)
MainIs: run_bench_hash.ml
BuildDepends: containers
PreBuildCommand: make qtest-gen
Executable run_qtest
Path: qtest/
Install: false
CompiledObject: best
MainIs: run_qtest.ml
Build$: flag(tests) && flag(unix)
BuildDepends: containers, containers.iter,
containers.sexp, containers.unix, containers.thread,
containers.data,
sequence, gen, unix, oUnit, qcheck
Test all
Command: ./run_qtest.native
TestTools: run_qtest
Run$: flag(tests) && flag(unix)
Executable id_sexp
Path: examples/
Install: false
CompiledObject: best
MainIs: id_sexp.ml
BuildDepends: containers.sexp
SourceRepository head
Type: git
Location: https://github.com/c-cube/ocaml-containers
Browser: https://github.com/c-cube/ocaml-containers/tree/master/src

163
_tags
View file

@ -1,163 +0,0 @@
# OASIS_START
# DO NOT EDIT (digest: 1681c391580688c2463b8457d464cf03)
# Ignore VCS directories, you can use the same kind of rule outside
# OASIS_START/STOP if you want to exclude directories that contains
# useless stuff for the build process
true: annot, bin_annot
<**/.svn>: -traverse
<**/.svn>: not_hygienic
".bzr": -traverse
".bzr": not_hygienic
".hg": -traverse
".hg": not_hygienic
".git": -traverse
".git": not_hygienic
"_darcs": -traverse
"_darcs": not_hygienic
# Library containers
"src/core/containers.cmxs": use_containers
<src/core/*.ml{,i,y}>: package(bytes)
<src/core/*.ml{,i,y}>: package(result)
# Library containers_io
"src/io/containers_io.cmxs": use_containers_io
<src/io/*.ml{,i,y}>: package(bytes)
# Library containers_unix
"src/unix/containers_unix.cmxs": use_containers_unix
<src/unix/*.ml{,i,y}>: package(bytes)
<src/unix/*.ml{,i,y}>: package(unix)
# Library containers_sexp
"src/sexp/containers_sexp.cmxs": use_containers_sexp
<src/sexp/*.ml{,i,y}>: package(bytes)
# Library containers_data
"src/data/containers_data.cmxs": use_containers_data
<src/data/*.ml{,i,y}>: package(bytes)
# Library containers_iter
"src/iter/containers_iter.cmxs": use_containers_iter
# Library containers_string
"src/string/containers_string.cmxs": use_containers_string
<src/string/*.ml{,i,y}>: package(bytes)
# Library containers_advanced
"src/advanced/containers_advanced.cmxs": use_containers_advanced
<src/advanced/*.ml{,i,y}>: package(bytes)
<src/advanced/*.ml{,i,y}>: package(result)
<src/advanced/*.ml{,i,y}>: package(sequence)
<src/advanced/*.ml{,i,y}>: use_containers
# Library containers_bigarray
"src/bigarray/containers_bigarray.cmxs": use_containers_bigarray
<src/bigarray/*.ml{,i,y}>: package(bigarray)
<src/bigarray/*.ml{,i,y}>: package(bytes)
<src/bigarray/*.ml{,i,y}>: package(result)
<src/bigarray/*.ml{,i,y}>: use_containers
# Library containers_thread
"src/threads/containers_thread.cmxs": use_containers_thread
<src/threads/*.ml{,i,y}>: package(bytes)
<src/threads/*.ml{,i,y}>: package(result)
<src/threads/*.ml{,i,y}>: package(threads)
<src/threads/*.ml{,i,y}>: use_containers
# Library containers_top
"src/top/containers_top.cmxs": use_containers_top
<src/top/*.ml{,i,y}>: package(bigarray)
<src/top/*.ml{,i,y}>: package(bytes)
<src/top/*.ml{,i,y}>: package(compiler-libs.common)
<src/top/*.ml{,i,y}>: package(result)
<src/top/*.ml{,i,y}>: package(unix)
<src/top/*.ml{,i,y}>: use_containers
<src/top/*.ml{,i,y}>: use_containers_bigarray
<src/top/*.ml{,i,y}>: use_containers_data
<src/top/*.ml{,i,y}>: use_containers_iter
<src/top/*.ml{,i,y}>: use_containers_sexp
<src/top/*.ml{,i,y}>: use_containers_string
<src/top/*.ml{,i,y}>: use_containers_unix
# Executable run_benchs
<benchs/run_benchs.{native,byte}>: package(benchmark)
<benchs/run_benchs.{native,byte}>: package(bytes)
<benchs/run_benchs.{native,byte}>: package(gen)
<benchs/run_benchs.{native,byte}>: package(hamt)
<benchs/run_benchs.{native,byte}>: package(result)
<benchs/run_benchs.{native,byte}>: package(sequence)
<benchs/run_benchs.{native,byte}>: package(threads)
<benchs/run_benchs.{native,byte}>: use_containers
<benchs/run_benchs.{native,byte}>: use_containers_advanced
<benchs/run_benchs.{native,byte}>: use_containers_data
<benchs/run_benchs.{native,byte}>: use_containers_iter
<benchs/run_benchs.{native,byte}>: use_containers_string
<benchs/run_benchs.{native,byte}>: use_containers_thread
<benchs/*.ml{,i,y}>: package(benchmark)
<benchs/*.ml{,i,y}>: package(gen)
<benchs/*.ml{,i,y}>: package(threads)
<benchs/*.ml{,i,y}>: use_containers_advanced
<benchs/*.ml{,i,y}>: use_containers_iter
<benchs/*.ml{,i,y}>: use_containers_string
<benchs/*.ml{,i,y}>: use_containers_thread
# Executable run_bench_hash
<benchs/run_bench_hash.{native,byte}>: package(bytes)
<benchs/run_bench_hash.{native,byte}>: package(result)
<benchs/run_bench_hash.{native,byte}>: use_containers
# Executable run_qtest
<qtest/run_qtest.{native,byte}>: package(QTest2Lib)
<qtest/run_qtest.{native,byte}>: package(bigarray)
<qtest/run_qtest.{native,byte}>: package(bytes)
<qtest/run_qtest.{native,byte}>: package(gen)
<qtest/run_qtest.{native,byte}>: package(oUnit)
<qtest/run_qtest.{native,byte}>: package(result)
<qtest/run_qtest.{native,byte}>: package(sequence)
<qtest/run_qtest.{native,byte}>: package(threads)
<qtest/run_qtest.{native,byte}>: package(unix)
<qtest/run_qtest.{native,byte}>: use_containers
<qtest/run_qtest.{native,byte}>: use_containers_advanced
<qtest/run_qtest.{native,byte}>: use_containers_bigarray
<qtest/run_qtest.{native,byte}>: use_containers_data
<qtest/run_qtest.{native,byte}>: use_containers_io
<qtest/run_qtest.{native,byte}>: use_containers_iter
<qtest/run_qtest.{native,byte}>: use_containers_sexp
<qtest/run_qtest.{native,byte}>: use_containers_string
<qtest/run_qtest.{native,byte}>: use_containers_thread
<qtest/run_qtest.{native,byte}>: use_containers_unix
<qtest/*.ml{,i,y}>: package(QTest2Lib)
<qtest/*.ml{,i,y}>: package(bigarray)
<qtest/*.ml{,i,y}>: package(bytes)
<qtest/*.ml{,i,y}>: package(gen)
<qtest/*.ml{,i,y}>: package(oUnit)
<qtest/*.ml{,i,y}>: package(result)
<qtest/*.ml{,i,y}>: package(sequence)
<qtest/*.ml{,i,y}>: package(threads)
<qtest/*.ml{,i,y}>: package(unix)
<qtest/*.ml{,i,y}>: use_containers
<qtest/*.ml{,i,y}>: use_containers_advanced
<qtest/*.ml{,i,y}>: use_containers_bigarray
<qtest/*.ml{,i,y}>: use_containers_data
<qtest/*.ml{,i,y}>: use_containers_io
<qtest/*.ml{,i,y}>: use_containers_iter
<qtest/*.ml{,i,y}>: use_containers_sexp
<qtest/*.ml{,i,y}>: use_containers_string
<qtest/*.ml{,i,y}>: use_containers_thread
<qtest/*.ml{,i,y}>: use_containers_unix
# Executable mem_measure
"benchs/mem_measure.native": package(bytes)
"benchs/mem_measure.native": package(hamt)
"benchs/mem_measure.native": package(result)
"benchs/mem_measure.native": package(sequence)
"benchs/mem_measure.native": package(unix)
"benchs/mem_measure.native": use_containers
"benchs/mem_measure.native": use_containers_data
<benchs/*.ml{,i,y}>: package(bytes)
<benchs/*.ml{,i,y}>: package(hamt)
<benchs/*.ml{,i,y}>: package(result)
<benchs/*.ml{,i,y}>: package(sequence)
<benchs/*.ml{,i,y}>: package(unix)
<benchs/*.ml{,i,y}>: use_containers
<benchs/*.ml{,i,y}>: use_containers_data
# Executable id_sexp
<examples/id_sexp.{native,byte}>: package(bytes)
<examples/id_sexp.{native,byte}>: use_containers_sexp
<examples/*.ml{,i,y}>: package(bytes)
<examples/*.ml{,i,y}>: use_containers_sexp
# OASIS_STOP
<tests/*.ml{,i}>: thread
<src/threads/*.ml{,i}>: thread
<src/core/CCVector.cmx> or <src/core/CCString.cmx>: inline(25)
<src/data/CCFlatHashtbl.cm*> or <src/data/CCHashTrie.cm*> or <src/data/CCPersistent*>: inline(15)
<src/core/CCMap.*> or <src/core/CCSet.*> or <src/core/CCList.*>: warn(-32)
<src/**/*.ml> and not <src/misc/*.ml>: warn(+a-4-44-58-60@8)
true: no_alias_deps, safe_string, short_paths, color(always)
<src/**/*Labels.cm*>: nolabels

24
benchs/dune Normal file
View file

@ -0,0 +1,24 @@
(executables
(names run_benchs run_bench_hash run_objsize)
(libraries
containers
containers_pvec
containers-data
benchmark
gen
iter
qcheck
oseq
batteries
base
sek)
(flags :standard -warn-error -3-5 -w -60 -safe-string -color always)
(optional)
(ocamlopt_flags
:standard
-O3
-color
always
-unbox-closures
-unbox-closures-factor
20))

View file

@ -22,50 +22,52 @@ open Obj
(*s Pointers already visited are stored in a hash-table, where (*s Pointers already visited are stored in a hash-table, where
comparisons are done using physical equality. *) comparisons are done using physical equality. *)
module H = Hashtbl.Make( module H = Hashtbl.Make (struct
struct type t = Obj.t
type t = Obj.t
let equal = (==) let equal = ( == )
let hash o = Hashtbl.hash (magic o : int) let hash o = Hashtbl.hash (magic o : int)
end) end)
let node_table = (H.create 257 : unit H.t) let node_table = (H.create 257 : unit H.t)
let in_table o = try H.find node_table o; true with Not_found -> false let in_table o =
try
H.find node_table o;
true
with Not_found -> false
let add_in_table o = H.add node_table o () let add_in_table o = H.add node_table o ()
let reset_table () = H.clear node_table let reset_table () = H.clear node_table
(*s Objects are traversed recursively, as soon as their tags are less than (*s Objects are traversed recursively, as soon as their tags are less than
[no_scan_tag]. [count] records the numbers of words already visited. *) [no_scan_tag]. [count] records the numbers of words already visited. *)
let size_of_double = size (repr 1.0) let size_of_double = size (repr 1.0)
let count = ref 0 let count = ref 0
let rec traverse t = let rec traverse t =
if not (in_table t) then begin if not (in_table t) then (
add_in_table t; add_in_table t;
if is_block t then begin if is_block t then (
let n = size t in let n = size t in
let tag = tag t in let tag = tag t in
if tag < no_scan_tag then begin if tag < no_scan_tag then (
count := !count + 1 + n; count := !count + 1 + n;
for i = 0 to n - 1 do for i = 0 to n - 1 do
let f = field t i in let f = field t i in
if is_block f then traverse f if is_block f then traverse f
done done
end else if tag = string_tag then ) else if tag = string_tag then
count := !count + 1 + n count := !count + 1 + n
else if tag = double_tag then else if tag = double_tag then
count := !count + size_of_double count := !count + size_of_double
else if tag = double_array_tag then else if tag = double_array_tag then
count := !count + 1 + size_of_double * n count := !count + 1 + (size_of_double * n)
else else
incr count incr count
end )
end )
(*s Sizes of objects in words and in bytes. The size in bytes is computed (*s Sizes of objects in words and in bytes. The size in bytes is computed
system-independently according to [Sys.word_size]. *) system-independently according to [Sys.word_size]. *)
@ -76,8 +78,5 @@ let size_w o =
traverse (repr o); traverse (repr o);
!count !count
let size_b o = (size_w o) * (Sys.word_size / 8) let size_b o = size_w o * (Sys.word_size / 8)
let size_kb o = size_w o / (8192 / Sys.word_size)
let size_kb o = (size_w o) / (8192 / Sys.word_size)

View file

@ -1,58 +1,55 @@
(* reference implementations for some structures, for comparison purpose *) (* reference implementations for some structures, for comparison purpose *)
module PersistentHashtbl(H : Hashtbl.HashedType) = struct module PersistentHashtbl (H : Hashtbl.HashedType) = struct
module Table = Hashtbl.Make(H) module Table = Hashtbl.Make (H)
(** Imperative hashtable *) (** Imperative hashtable *)
type key = H.t type key = H.t
type 'a t = 'a zipper ref type 'a t = 'a zipper ref
and 'a zipper = and 'a zipper =
| Table of 'a Table.t (** Concrete table *) | Table of 'a Table.t (** Concrete table *)
| Add of key * 'a * 'a t (** Add key *) | Add of key * 'a * 'a t (** Add key *)
| Replace of key * 'a * 'a t (** Replace key by value *) | Replace of key * 'a * 'a t (** Replace key by value *)
| Remove of key * 'a t (** As the table, but without given key *) | Remove of key * 'a t (** As the table, but without given key *)
let create i =
ref (Table (Table.create i))
let create i = ref (Table (Table.create i))
let empty () = create 11 let empty () = create 11
(* pass continuation to get a tailrec rerooting *) (* pass continuation to get a tailrec rerooting *)
let rec _reroot t k = match !t with let rec _reroot t k =
| Table tbl -> k tbl (* done *) match !t with
| Add (key, v, t') -> | Table tbl -> k tbl (* done *)
_reroot t' | Add (key, v, t') ->
(fun tbl -> _reroot t' (fun tbl ->
t' := Remove (key, t); t' := Remove (key, t);
Table.add tbl key v; Table.add tbl key v;
t := Table tbl; t := Table tbl;
k tbl) k tbl)
| Replace (key, v, t') -> | Replace (key, v, t') ->
_reroot t' _reroot t' (fun tbl ->
(fun tbl -> let v' = Table.find tbl key in
let v' = Table.find tbl key in t' := Replace (key, v', t);
t' := Replace (key, v', t); t := Table tbl;
t := Table tbl; Table.replace tbl key v;
Table.replace tbl key v; k tbl)
k tbl) | Remove (key, t') ->
| Remove (key, t') -> _reroot t' (fun tbl ->
_reroot t' let v = Table.find tbl key in
(fun tbl -> t' := Add (key, v, t);
let v = Table.find tbl key in t := Table tbl;
t' := Add (key, v, t); Table.remove tbl key;
t := Table tbl; k tbl)
Table.remove tbl key;
k tbl)
(* Reroot: modify the zipper so that the current node is a proper (* Reroot: modify the zipper so that the current node is a proper
hashtable, and return the hashtable *) hashtable, and return the hashtable *)
let reroot t = match !t with let reroot t =
match !t with
| Table tbl -> tbl | Table tbl -> tbl
| _ -> _reroot t (fun x -> x) | _ -> _reroot t (fun x -> x)
let is_empty t = Table.length (reroot t) = 0 let is_empty t = Table.length (reroot t) = 0
let find t k = Table.find (reroot t) k let find t k = Table.find (reroot t) k
(*$R (*$R
@ -91,13 +88,8 @@ module PersistentHashtbl(H : Hashtbl.HashedType) = struct
*) *)
let get_exn k t = find t k let get_exn k t = find t k
let get k t = try Some (find t k) with Not_found -> None
let get k t =
try Some (find t k)
with Not_found -> None
let mem t k = Table.mem (reroot t) k let mem t k = Table.mem (reroot t) k
let length t = Table.length (reroot t) let length t = Table.length (reroot t)
(*$R (*$R
@ -120,11 +112,9 @@ module PersistentHashtbl(H : Hashtbl.HashedType) = struct
let t' = ref (Table tbl) in let t' = ref (Table tbl) in
(* update [t] to point to the new hashtable *) (* update [t] to point to the new hashtable *)
(try (try
let v' = Table.find tbl k in let v' = Table.find tbl k in
t := Replace (k, v', t') t := Replace (k, v', t')
with Not_found -> with Not_found -> t := Remove (k, t'));
t := Remove (k, t')
);
(* modify the underlying hashtable *) (* modify the underlying hashtable *)
Table.replace tbl k v; Table.replace tbl k v;
t' t'
@ -170,12 +160,12 @@ module PersistentHashtbl(H : Hashtbl.HashedType) = struct
let h = H.of_list l in let h = H.of_list l in
let h = List.fold_left (fun h (k,_) -> H.remove h k) h l in let h = List.fold_left (fun h (k,_) -> H.remove h k) h l in
H.is_empty h) H.is_empty h)
*) *)
let update t k f = let update t k f =
let v = get k t in let v = get k t in
match v, f v with match v, f v with
| None, None -> t (* no change *) | None, None -> t (* no change *)
| Some _, None -> remove t k | Some _, None -> remove t k
| _, Some v' -> replace t k v' | _, Some v' -> replace t k v'
@ -209,10 +199,11 @@ module PersistentHashtbl(H : Hashtbl.HashedType) = struct
let tbl = reroot t in let tbl = reroot t in
let res = Table.create (Table.length tbl) in let res = Table.create (Table.length tbl) in
Table.iter Table.iter
(fun k v -> match f k v with (fun k v ->
match f k v with
| None -> () | None -> ()
| Some v' -> Table.replace res k v' | Some v' -> Table.replace res k v')
) tbl; tbl;
ref (Table res) ref (Table res)
exception ExitPTbl exception ExitPTbl
@ -231,17 +222,17 @@ module PersistentHashtbl(H : Hashtbl.HashedType) = struct
let merge f t1 t2 = let merge f t1 t2 =
let tbl = Table.create (max (length t1) (length t2)) in let tbl = Table.create (max (length t1) (length t2)) in
iter t1 iter t1 (fun k v1 ->
(fun k v1 ->
let v2 = try Some (find t2 k) with Not_found -> None in let v2 = try Some (find t2 k) with Not_found -> None in
match f k (Some v1) v2 with match f k (Some v1) v2 with
| None -> () | None -> ()
| Some v' -> Table.replace tbl k v'); | Some v' -> Table.replace tbl k v');
iter t2 iter t2 (fun k v2 ->
(fun k v2 -> if not (mem t1 k) then (
if not (mem t1 k) then match f k None (Some v2) with match f k None (Some v2) with
| None -> () | None -> ()
| Some _ -> Table.replace tbl k v2); | Some _ -> Table.replace tbl k v2
));
ref (Table tbl) ref (Table tbl)
(*$R (*$R
@ -262,13 +253,11 @@ module PersistentHashtbl(H : Hashtbl.HashedType) = struct
let add_seq init seq = let add_seq init seq =
let tbl = ref init in let tbl = ref init in
seq (fun (k,v) -> tbl := replace !tbl k v); seq (fun (k, v) -> tbl := replace !tbl k v);
!tbl !tbl
let of_seq seq = add_seq (empty ()) seq let of_seq seq = add_seq (empty ()) seq
let add_list init l = add_seq init (fun k -> List.iter k l)
let add_list init l =
add_seq init (fun k -> List.iter k l)
(*$QR (*$QR
_list_int_int (fun l -> _list_int_int (fun l ->
@ -293,7 +282,7 @@ module PersistentHashtbl(H : Hashtbl.HashedType) = struct
let to_list t = let to_list t =
let tbl = reroot t in let tbl = reroot t in
let bindings = Table.fold (fun k v acc -> (k,v)::acc) tbl [] in let bindings = Table.fold (fun k v acc -> (k, v) :: acc) tbl [] in
bindings bindings
(*$R (*$R
@ -302,10 +291,9 @@ module PersistentHashtbl(H : Hashtbl.HashedType) = struct
OUnit.assert_equal my_list (List.sort compare l) OUnit.assert_equal my_list (List.sort compare l)
*) *)
let to_seq t = let to_seq t k =
fun k -> let tbl = reroot t in
let tbl = reroot t in Table.iter (fun x y -> k (x, y)) tbl
Table.iter (fun x y -> k (x,y)) tbl
(*$R (*$R
let h = H.of_seq my_seq in let h = H.of_seq my_seq in
@ -316,31 +304,34 @@ module PersistentHashtbl(H : Hashtbl.HashedType) = struct
let equal eq t1 t2 = let equal eq t1 t2 =
length t1 = length t2 length t1 = length t2
&& && for_all
for_all (fun k v ->
(fun k v -> match get k t2 with match get k t2 with
| None -> false | None -> false
| Some v' -> eq v v' | Some v' -> eq v v')
) t1 t1
let pp pp_k pp_v buf t = let pp pp_k pp_v buf t =
Buffer.add_string buf "{"; Buffer.add_string buf "{";
let first = ref true in let first = ref true in
iter t iter t (fun k v ->
(fun k v -> if !first then
if !first then first:=false else Buffer.add_string buf ", "; first := false
Printf.bprintf buf "%a -> %a" pp_k k pp_v v else
); Buffer.add_string buf ", ";
Printf.bprintf buf "%a -> %a" pp_k k pp_v v);
Buffer.add_string buf "}" Buffer.add_string buf "}"
let print pp_k pp_v fmt t = let print pp_k pp_v fmt t =
Format.pp_print_string fmt "{"; Format.pp_print_string fmt "{";
let first = ref true in let first = ref true in
iter t iter t (fun k v ->
(fun k v -> if !first then
if !first then first:=false first := false
else (Format.pp_print_string fmt ", "; Format.pp_print_cut fmt ()); else (
Format.fprintf fmt "%a -> %a" pp_k k pp_v v Format.pp_print_string fmt ", ";
); Format.pp_print_cut fmt ()
);
Format.fprintf fmt "%a -> %a" pp_k k pp_v v);
Format.pp_print_string fmt "}" Format.pp_print_string fmt "}"
end end

View file

@ -4,46 +4,39 @@ type tree =
| Empty | Empty
| Node of int * tree list | Node of int * tree list
let mk_node i l = Node (i,l) let mk_node i l = Node (i, l)
let random_tree = let random_tree =
CCRandom.(fix
~base:(return Empty)
~subn:[int 10, (fun sublist -> pure mk_node <*> small_int <*> sublist)]
(int_range 15 150)
)
let random_list =
CCRandom.( CCRandom.(
int 5 >>= fun len -> fix ~base:(return Empty)
CCList.random_len len random_tree ~subn:[ (int 10, fun sublist -> pure mk_node <*> small_int <*> sublist) ]
) (int_range 15 150))
let rec eq t1 t2 = match t1, t2 with let rec eq t1 t2 =
match t1, t2 with
| Empty, Empty -> true | Empty, Empty -> true
| Node(i1,l1), Node (i2,l2) -> i1=i2 && CCList.equal eq l1 l2 | Node (i1, l1), Node (i2, l2) -> i1 = i2 && CCList.equal eq l1 l2
| Node _, _ | Node _, _ | _, Node _ -> false
| _, Node _ -> false
let rec hash_tree t = match t with let rec hash_tree t =
match t with
| Empty -> CCHash.string "empty" | Empty -> CCHash.string "empty"
| Node (i, l) -> | Node (i, l) -> CCHash.(combine2 (int i) (list hash_tree l))
CCHash.(combine2 (int i) (list hash_tree l))
module H = Hashtbl.Make(struct module H = Hashtbl.Make (struct
type t = tree type t = tree
let equal = eq let equal = eq
let hash = hash_tree let hash = hash_tree
end) end)
let print_hashcons_stats st = let print_hashcons_stats st =
let open Hashtbl in let open Hashtbl in
Format.printf Format.printf "tbl stats: %d elements, num buckets: %d, max bucket: %d@."
"tbl stats: %d elements, num buckets: %d, max bucket: %d@." st.num_bindings st.num_buckets st.max_bucket_length;
st.num_bindings st.num_buckets st.max_bucket_length; Array.iteri
Array.iteri (fun i n -> Format.printf " %d\t buckets have length %d@." n i)
(fun i n -> Format.printf " %d\t buckets have length %d@." n i) st.bucket_histogram
st.bucket_histogram
let () = let () =
let st = Random.State.make_self_init () in let st = Random.State.make_self_init () in
@ -61,4 +54,3 @@ let () =
List.iter (fun t -> Hashtbl.replace tbl' t ()) l; List.iter (fun t -> Hashtbl.replace tbl' t ()) l;
print_hashcons_stats (Hashtbl.stats tbl'); print_hashcons_stats (Hashtbl.stats tbl');
() ()

File diff suppressed because it is too large Load diff

3
benchs/run_benchs.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
exec dune exec --profile=release benchs/run_benchs.exe -- $@

94
benchs/run_objsize.ml Normal file
View file

@ -0,0 +1,94 @@
(* module Deque = Core_kernel.Deque *)
module Int_map = CCMap.Make (CCInt)
module Int_set = CCSet.Make (CCInt)
let dup = CCPair.dup
let id = CCFun.id
let ns n = List.init n CCFun.id
let iter_range n f = List.iter f (ns n)
let gen_cons x xs =
let saw_x = ref false in
fun () ->
if !saw_x then (
saw_x := true;
Some x
) else
xs ()
let front = Sek.front
let dummy = 0
let types =
[
("Stdlib.List", fun n -> Obj.magic @@ ns n);
("Stdlib.Array", fun n -> Obj.magic @@ Array.init n id);
( "Stdlib.Hashtbl",
fun n -> Obj.magic @@ CCHashtbl.of_iter Iter.(init dup |> take n) );
( "Base.Hashtbl",
fun n -> Obj.magic @@ Base.Hashtbl.Poly.of_alist_exn (List.init n dup) );
( "Stdlib.Map",
fun n -> Obj.magic @@ Int_map.of_iter Iter.(init dup |> take n) );
( "Stdlib.Set",
fun n -> Obj.magic @@ Int_set.of_iter Iter.(init id |> take n) );
("CCFun_vec", fun n -> Obj.magic @@ CCFun_vec.of_list (ns n));
("CCRAL", fun n -> Obj.magic @@ CCRAL.of_list (ns n));
("BatVect", fun n -> Obj.magic @@ BatVect.of_list (ns n));
( "Sek.Persistent",
fun n ->
Obj.magic
@@ List.fold_left
(Sek.Persistent.push front)
(Sek.Persistent.create dummy)
(ns n) );
( "Sek.Ephemeral",
fun n ->
Obj.magic
@@
let c = Sek.Ephemeral.create dummy in
iter_range n (Sek.Ephemeral.push front c);
c );
( "CCVector",
fun n ->
Obj.magic
@@
let c = CCVector.create () in
iter_range n (CCVector.push c);
c );
(* "Core_kernel.Deque", (fun n -> Obj.magic @@ let c = Deque.create () in iter_range n (Deque.enqueue_back c); c); *)
( "Base.Queue",
fun n ->
Obj.magic
@@
let c = Base.Queue.create () in
iter_range n (Base.Queue.enqueue c);
c );
( "Stdlib.Queue",
fun n ->
Obj.magic
@@
let q = Queue.create () in
iter_range n (fun x -> Queue.push x q);
q );
("CCQueue", fun n -> Obj.magic @@ CCDeque.of_list (ns n));
("Iter", fun n -> Obj.magic @@ List.fold_right Iter.cons (ns n) Iter.empty);
("Gen", fun n -> Obj.magic @@ List.fold_right gen_cons (ns n) Gen.empty);
( "Stdlib.Seq",
fun n -> Obj.magic @@ List.fold_right OSeq.cons (ns n) OSeq.empty );
]
let () =
let sizes = [ 0; 1; 10; 100; 1000; 10000 ] in
Printf.printf "%-20s " "";
sizes |> List.iter (fun n -> Printf.printf "%6d " n);
Printf.printf "\n";
types
|> List.iter (fun (name, create) ->
Printf.printf "%-20s: " name;
sizes
|> List.iter (fun n ->
let obj = create n in
let size = Objsize.size_w obj in
(* let size = Obj.reachable_words (Obj.repr obj) in *)
Printf.printf "%6d " size);
Printf.printf "\n")

31
configure vendored
View file

@ -1,31 +0,0 @@
#!/bin/sh
# OASIS_START
# DO NOT EDIT (digest: 7577949ceda6f9dbd4983aea8db9275b)
set -e
FST=true
for i in "$@"; do
if $FST; then
set --
FST=false
fi
case $i in
--*=*)
ARG=${i%%=*}
VAL=${i##*=}
set -- "$@" "$ARG" "$VAL"
;;
*)
set -- "$@" "$i"
;;
esac
done
if [ ! -e setup.exe ] || [ _oasis -nt setup.exe ] || [ setup.ml -nt setup.exe ] || [ configure -nt setup.exe ]; then
ocamlfind ocamlopt -o setup.exe -linkpkg -package oasis.dynrun setup.ml || ocamlfind ocamlc -o setup.exe -linkpkg -package oasis.dynrun setup.ml || exit 1
rm -f setup.cmi setup.cmo setup.cmx setup.o
fi
./setup.exe -configure "$@"
# OASIS_STOP

26
containers-data.opam Normal file
View file

@ -0,0 +1,26 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
version: "3.17"
synopsis: "A set of advanced datatypes for containers"
maintainer: ["c-cube"]
authors: ["c-cube"]
license: "BSD-2-Clause"
tags: ["containers" "RAL" "function" "vector" "okasaki"]
homepage: "https://github.com/c-cube/ocaml-containers/"
bug-reports: "https://github.com/c-cube/ocaml-containers/issues"
depends: [
"dune" {>= "3.0"}
"ocaml" {>= "4.08"}
"containers" {= version}
"qcheck-core" {>= "0.18" & with-test}
"iter" {with-test}
"gen" {with-test}
"mdx" {with-test}
"odoc" {with-doc}
]
dev-repo: "git+https://github.com/c-cube/ocaml-containers.git"
build: [
["dune" "build" "-p" name "-j" jobs]
["dune" "build" "@doc" "-p" name ] {with-doc}
["dune" "runtest" "-p" name "-j" jobs] {with-test & arch != "x86_32" & arch != "arm32"}
]

View file

@ -0,0 +1,5 @@
build: [
["dune" "build" "-p" name "-j" jobs]
["dune" "build" "@doc" "-p" name ] {with-doc}
["dune" "runtest" "-p" name "-j" jobs] {with-test & arch != "x86_32" & arch != "arm32"}
]

31
containers.opam Normal file
View file

@ -0,0 +1,31 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
version: "3.17"
synopsis:
"A modular, clean and powerful extension of the OCaml standard library"
maintainer: ["c-cube"]
authors: ["c-cube"]
license: "BSD-2-Clause"
tags: ["stdlib" "containers" "iterators" "list" "heap" "queue"]
homepage: "https://github.com/c-cube/ocaml-containers/"
bug-reports: "https://github.com/c-cube/ocaml-containers/issues"
depends: [
"dune" {>= "3.0"}
"ocaml" {>= "4.08"}
"either"
"dune-configurator"
"qcheck-core" {>= "0.18" & with-test}
"yojson" {with-test}
"iter" {with-test}
"gen" {with-test}
"csexp" {with-test}
"uutf" {with-test}
"odoc" {with-doc}
]
depopts: ["base-unix" "base-threads"]
dev-repo: "git+https://github.com/c-cube/ocaml-containers.git"
build: [
["dune" "build" "-p" name "-j" jobs]
["dune" "build" "@doc" "-p" name ] {with-doc}
["dune" "runtest" "-p" name "-j" jobs] {with-test & arch != "x86_32" & arch != "arm32"}
]

5
containers.opam.template Normal file
View file

@ -0,0 +1,5 @@
build: [
["dune" "build" "-p" name "-j" jobs]
["dune" "build" "@doc" "-p" name ] {with-doc}
["dune" "runtest" "-p" name "-j" jobs] {with-test & arch != "x86_32" & arch != "arm32"}
]

View file

@ -1,32 +0,0 @@
#!/usr/bin/env ocaml
(* note: this requires to generate documentation first, so that
.odoc files are generated *)
#use "topfind";;
#require "containers";;
#require "containers.io";;
#require "gen";;
#require "unix";;
let odoc_files =
CCIO.File.walk "_build"
|> Gen.filter_map
(function
| `File, f when CCString.suffix ~suf:".odoc" f -> Some f
| _ -> None
)
|> Gen.flat_map
(fun f -> Gen.of_list ["-load"; f])
|> Gen.to_list
;;
let out = "deps.dot";;
let cmd =
"ocamldoc -dot -o " ^ out ^ " " ^ String.concat " " odoc_files
;;
print_endline ("run: " ^ cmd);;
Unix.system cmd;;
print_endline ("output in " ^ out);;

View file

@ -1,12 +1,12 @@
= OCaml-containers = # More about OCaml-containers
:toc: macro
:source-highlighter: pygments
This document contains more information on some modules of Containers. This document contains more information on some modules of Containers.
toc::[] ```ocaml
# #require "containers";;
```
== Hash combinators: `CCHash` ## Hash combinators: `CCHash`
Although OCaml provides polymorphic hash tables (`('a,'b) Hashtbl.t`) Although OCaml provides polymorphic hash tables (`('a,'b) Hashtbl.t`)
using the polymorphic equality `(=)` and hash `Hashtbl.hash`, it is often using the polymorphic equality `(=)` and hash `Hashtbl.hash`, it is often
@ -15,63 +15,67 @@ with custom equality and hash functions.
`CCHash` provides combinators for writing hash functions: `CCHash` provides combinators for writing hash functions:
[source,OCaml] ```ocaml
----
# module H = CCHash;; # module H = CCHash;;
module H = CCHash
# let hash1 : (int * bool) list H.t = H.(list (pair int bool));; # let hash1 : (int * bool) list H.t = H.(list (pair int bool));;
val hash1 : (int * bool) list H.t = <fun>
```
```ocaml non-deterministic=output
# hash1 [1, true; 2, false; 3, true];; # hash1 [1, true; 2, false; 3, true];;
- : int = 636041136 - : int = 636041136
(* the function hashes the whole value, can be costly *)
# hash1 CCList.(1 -- 1000 |> map (fun i->i, i mod 2 = 0));; # hash1 CCList.(1 -- 1000 |> map (fun i->i, i mod 2 = 0));;
- : int = 845685523 - : int = 845685523
# hash1 CCList.(1 -- 1001 |> map (fun i->i, i mod 2 = 0));; # hash1 CCList.(1 -- 1001 |> map (fun i->i, i mod 2 = 0));;
- : int = 381026697 - : int = 381026697
---- ```
The polymorphic hash function is still present, as `CCHash.poly`. The polymorphic hash function is still present, as `CCHash.poly`.
The functions `CCHash.list_comm` and `CCHash.array_comm` allow to hash The functions `CCHash.list_comm` and `CCHash.array_comm` allow to hash
lists and arrays while ignoring the order of elements: all permutations lists and arrays while ignoring the order of elements: all permutations
of the input will have the same hash. of the input will have the same hash.
## Parser Combinator: `CCParse`
== Parser Combinator: `CCParse`
:toc: macro
:source-highlighter: pygments
The module `CCParse` defines basic parser combinators on strings. The module `CCParse` defines basic parser combinators on strings.
Adapting https://github.com/inhabitedtype/angstrom#usage[angstrom's tutorial example] gives the following snippet. Adapting [angstrom's tutorial example](https://github.com/inhabitedtype/angstrom#usage)
gives the following snippet.
Note that backtracking is explicit in `CCParse`, hence Note that backtracking is explicit in `CCParse`, hence
the use of `try_` to allow it in some places. the use of `try_` to allow it in some places.
Explicit memoization with `memo` and `fix_memo` is also possible. Explicit memoization with `memo` and `fix_memo` is also possible.
[source,OCaml] ```ocaml
---- open CCParse.Infix
open CCParse.Infix;; module P = CCParse
module P = CCParse;;
let parens p = P.try_ (P.char '(') *> p <* P.char ')' ;; let parens p = P.try_ (P.char '(') *> p <* P.char ')'
let add = P.char '+' *> P.return (+) ;; let add = P.char '+' *> P.return (+)
let sub = P.char '-' *> P.return (-) ;; let sub = P.char '-' *> P.return (-)
let mul = P.char '*' *> P.return ( * ) ;; let mul = P.char '*' *> P.return ( * )
let div = P.char '/' *> P.return ( / ) ;; let div = P.char '/' *> P.return ( / )
let integer = let integer =
P.chars1_if (function '0'..'9'->true|_->false) >|= int_of_string ;; P.chars1_if (function '0'..'9'->true|_->false) >|= int_of_string
let chainl1 e op = let chainl1 e op =
P.fix (fun r -> P.fix (fun r ->
e >>= fun x -> P.try_ (op <*> P.return x <*> r) <|> P.return x) ;; e >>= fun x -> P.try_ (op <*> P.return x <*> r) <|> P.return x)
let expr : int P.t = let expr : int P.t =
P.fix (fun expr -> P.fix (fun expr ->
let factor = parens expr <|> integer in let factor = parens expr <|> integer in
let term = chainl1 factor (mul <|> div) in let term = chainl1 factor (mul <|> div) in
chainl1 term (add <|> sub)) ;; chainl1 term (add <|> sub))
```
P.parse_string expr "4*1+2";; (* Ok 6 *) Now we can parse strings using `expr`:
P.parse_string expr "4*(1+2)";; (* Ok 12 *)
```ocaml
# P.parse_string expr "4*1+2";; (* Ok 6 *)
- : int P.or_error = Result.Ok 6
# P.parse_string expr "4*(1+2)";; (* Ok 12 *)
- : int P.or_error = Result.Ok 12
```
----

View file

@ -1,166 +0,0 @@
{1 Containers}
{2 Change Log}
See {{: https://github.com/c-cube/ocaml-containers/blob/master/CHANGELOG.adoc } this file}
{2 License}
This code is free, under the BSD license.
The logo (media/logo.png) is
CC-SA3 {{:http://en.wikipedia.org/wiki/File:Hypercube.svg} wikimedia}
{2 Contents}
The design is mostly centered around polymorphism rather than functors. Such
structures comprise (some modules in misc/, some other in core/):
the core library, containers, now depends on base-bytes (provided
by ocamlfind).
{4 Core Modules (extension of the standard library)}
{b findlib name}: containers
{!modules:
CCArray
CCArrayLabels
CCArray_slice
CCBool
CCChar
CCEqual
CCFloat
CCFormat
CCFun
CCHash
CCHashtbl
CCHeap
CCIO
CCInt
CCInt64
CCList
CCListLabels
CCMap
CCOpt
CCOrd
CCPair
CCParse
CCRandom
CCRef
CCResult
CCSet
CCString
CCVector
Containers
}
The module {!Containers} contains aliases to most other modules defined
in {i containers core}, and mixins
such as:
{[ module List = struct
include List
include CCList
end
]}
{4 Containers.data}
{b findlib name}: containers.data
Various data structures.
{!modules:
CCBitField
CCBV
CCCache
CCDeque
CCFQueue
CCFlatHashtbl
CCGraph
CCHashSet
CCHashTrie
CCHet
CCImmutArray
CCIntMap
CCMixmap
CCMixset
CCMixtbl
CCMultiMap
CCMultiSet
CCPersistentArray
CCPersistentHashtbl
CCRAL
CCRingBuffer
CCSimple_queue
CCTrie
CCWBTree
}
{4 Containers.unix}
Helpers that depend on {!Unix}, e.g. to spawn sub-processes.
{!modules: CCUnix}
{4 Containers.sexp}
A small S-expression library. The interface is relatively unstable, but
the main type ([CCSexp.t]) isn't.
{!modules:
CCSexp
}
{4 Containers.iter}
Iterators:
{!modules:
CCKList
CCKTree
CCLazy_list}
{4 String}
containers.string has been removed. Some of its functionality is present
in {!CCString}; some in other libraries such as [Spelll].
{4 Bigarrays}
containers.bigarray has been removed. Use the [Bigstring] library for
arrays of bytes.
{4 Advanced}
containers.advanced has been removed. Use [OLinq] to replace some of its
functionality.
{4 Misc}
Moved to its own repository.
{4 Lwt}
Moved to its own repository
{4 Thread Helpers}
{b findlib name}: containers.thread
Modules related to the use of [Thread].
{!modules:
CCBlockingQueue
CCLock
CCPool
CCSemaphore
CCThread
CCTimer
}
{2 Index}
{!indexlist}

41
dune Normal file
View file

@ -0,0 +1,41 @@
(rule
(targets README.md.corrected)
(deps
(package containers-data)
./src/mdx_runner.exe)
(enabled_if
(= %{system} "linux"))
(action
(run ./src/mdx_runner.exe)))
(rule
(alias runtest)
(package containers-data)
(enabled_if
(= %{system} "linux"))
(locks /ctest)
(action
(diff README.md README.md.corrected)))
(env
(_
(flags
:standard
-warn-error
-a+8
-w
-32-48-60-70
-w
+a-4-40-42-44-70
-color
always
-safe-string
-strict-sequence)
(ocamlopt_flags
:standard
-O3
-unbox-closures
-unbox-closures-factor
20
-inline
100)))

60
dune-project Normal file
View file

@ -0,0 +1,60 @@
(lang dune 3.0)
(name containers)
(generate_opam_files true)
(version 3.17)
(authors c-cube)
(maintainers c-cube)
(license BSD-2-Clause)
(homepage "https://github.com/c-cube/ocaml-containers/")
(source
(github c-cube/ocaml-containers))
(package
(name containers)
(synopsis
"A modular, clean and powerful extension of the OCaml standard library")
(tags
(stdlib containers iterators list heap queue))
(depends
(ocaml
(>= 4.08))
either
dune-configurator
(qcheck-core
(and
(>= 0.18)
:with-test))
(yojson :with-test)
(iter :with-test)
(gen :with-test)
(csexp :with-test)
(uutf :with-test)
(odoc :with-doc))
(depopts base-unix base-threads))
(package
(name containers-data)
(synopsis "A set of advanced datatypes for containers")
(tags
(containers RAL function vector okasaki))
(depends
(ocaml
(>= 4.08))
(containers
(= :version))
(qcheck-core
(and
(>= 0.18)
:with-test))
(iter :with-test)
(gen :with-test)
(mdx :with-test)
(odoc :with-doc)))

View file

@ -1,22 +0,0 @@
(** Write 10_000 Bencode values on the given file *)
(* write n times the same value in the file *)
let write_values file n =
let out = BencodeOnDisk.open_out file in
Printf.printf "[%d] opened file\n" (Unix.getpid ());
let v = Bencode.(L [I 0; I 1; S "foo"]) in
for i = 0 to n-1 do
Printf.printf "[%d] iteration %d\n" (Unix.getpid ()) i;
flush stdout;
BencodeOnDisk.write out v;
done;
BencodeOnDisk.close_out out;
Printf.printf "done\n";
()
let _ =
let file = Sys.argv.(1) in
Printf.printf "[%d] start: write to %s\n" (Unix.getpid ()) file;
flush stdout;
write_values file 100

View file

@ -0,0 +1,80 @@
(* parse IRC logs *)
type datetime = {
year: int;
month: int;
day: int;
hour: int;
min: int;
sec: int;
}
let pp_datetime out d =
let { year; month; day; hour; min; sec } = d in
CCFormat.(
fprintf out "{y=%d;M=%d;d=%d;h=%d;m=%d;s=%d}" year month day hour min sec)
type msg = {
timestamp: datetime;
user: string;
msg: string;
}
let pp_msg out m =
CCFormat.fprintf out "{@[time=%a;@ user=%S;@ msg=%S@]}" pp_datetime
m.timestamp m.user m.msg
open CCParse
let p_datetime : datetime t =
let int = U.int in
let* date, time = split_2 ~on_char:' ' in
let* y, m, d = recurse date (split_3 ~on_char:'-') in
let* year = recurse y int in
let* month = recurse m int in
let* day = recurse d int in
let* hour, min, sec =
recurse time
(let* hour = int in
char ':'
*> let* min = int in
char ':'
*> let+ sec = int in
hour, min, sec)
in
let dt = { year; month; day; hour; min; sec } in
return dt
let p_line =
let* line = lookahead all in
if Slice.is_empty line then
return None
else
let* fields = split_list ~on_char:'\t' in
match fields with
| [ date; user; rest ] ->
let+ timestamp = recurse date p_datetime
and+ user =
recurse user
(chars_if (function
| '>' -> false
| _ -> true))
and+ msg = recurse rest (all_str >|= String.trim) in
Some { timestamp; user; msg }
| _ ->
failf "expected 3 fields, got [%s]"
(String.concat ";" @@ List.map String.escaped
@@ List.map Slice.to_string fields)
let p_file = each_line (parsing "line" p_line) >|= CCList.keep_some
let () =
let s = CCIO.File.read_exn Sys.argv.(1) in
match parse_string p_file s with
| Ok l ->
Format.printf "parsed:@.";
List.iter (Format.printf "%a@." pp_msg) l
| Error e ->
Format.printf "parse error: %s@." e;
exit 1

73
examples/ccparse_sexp.ml Normal file
View file

@ -0,0 +1,73 @@
open CCParse
type sexp =
| Atom of string
| List of sexp list
let rec pp_sexpr out (s : sexp) : unit =
match s with
| Atom s -> Format.fprintf out "%S" s
| List l ->
Format.fprintf out "(@[";
List.iteri
(fun i s ->
if i > 0 then Format.fprintf out "@ ";
pp_sexpr out s)
l;
Format.fprintf out "@])"
let skip_white_and_comments =
fix @@ fun self ->
skip_white
*> try_or (char ';')
~f:(fun _ ->
skip_chars (function
| '\n' -> false
| _ -> true)
*> self)
~else_:(return ())
let atom =
chars_fold_transduce `Start ~f:(fun acc c ->
match acc, c with
| `Start, '"' -> `Continue `In_quote
| `Start, (' ' | '\t' | '\n' | '(' | ')' | ';') -> `Fail "atom"
| `Normal, (' ' | '\t' | '\n' | '(' | ')' | ';') -> `Stop
| `Done, _ -> `Stop
| `In_quote, '"' -> `Continue `Done (* consume *)
| `In_quote, '\\' -> `Continue `Escape
| `In_quote, c -> `Yield (`In_quote, c)
| `Escape, 'n' -> `Yield (`In_quote, '\n')
| `Escape, 't' -> `Yield (`In_quote, '\t')
| `Escape, '"' -> `Yield (`In_quote, '"')
| `Escape, '\\' -> `Yield (`In_quote, '\\')
| `Escape, c -> `Fail (Printf.sprintf "unknown escape code \\%c" c)
| (`Start | `Normal), c -> `Yield (`Normal, c)
| _ -> `Fail "invalid atom")
>>= function
| `In_quote, _ -> fail "unclosed \""
| `Escape, _ -> fail "unfinished escape sequence"
| _, "" -> fail "expected non-empty atom"
| _, s -> return (Atom s)
let psexp =
fix @@ fun self ->
skip_white_and_comments
*> try_or (char '(')
~f:(fun _ ->
sep ~by:skip_white_and_comments self
<* skip_white_and_comments <* char ')'
>|= fun l -> List l)
~else_:atom
let psexp_l = many_until ~until:(skip_white_and_comments *> eoi) psexp
let () =
let s = CCIO.File.read_exn Sys.argv.(1) in
match parse_string psexp_l s with
| Ok l ->
Format.printf "parsed:@.";
List.iter (Format.printf "%a@." pp_sexpr) l
| Error e ->
Format.printf "parse error: %s@." e;
exit 1

View file

@ -1,26 +0,0 @@
(** Export the list of files in a directory *)
let dir = "/tmp/"
(* list of files in a dir *)
let lsdir dir =
let d = Unix.opendir dir in
let l = ref [] in
begin try while true do
l := Unix.readdir d :: !l
done with End_of_file -> Unix.closedir d
end;
!l
let export dir =
let l = lsdir dir in
ToWeb.HTML.(concat
[ h1 (str ("files in "^ dir))
; list (List.map str l)
])
let state = ToWeb.State.create dir ~export
let _ =
ToWeb.serve_state ~sockfile:"/tmp/foo.sock" state

View file

@ -1,20 +0,0 @@
(** Display the graph of the collatz conjecture, starting from the given int *)
let g = LazyGraph.map
~edges:(fun () -> [])
~vertices:(fun i -> [`Label (string_of_int i)])
LazyGraph.collatz_graph
let collatz n filename =
Format.printf "print graph to %s@." filename;
let out = open_out filename in
let fmt = Format.formatter_of_out_channel out in
LazyGraph.Dot.pp ~name:"collatz" g fmt (Sequence.singleton n);
Format.pp_print_flush fmt ();
close_out out
let _ =
if Array.length Sys.argv < 3
then (Format.printf "use: collatz num file@."; exit 0)
else collatz (int_of_string Sys.argv.(1)) Sys.argv.(2)

View file

@ -1,83 +0,0 @@
(** Crawl the web to find shortest path between two urls *)
open Batteries
let pool = Future.Pool.create ~timeout:15. ~size:50
let split_lines s = String.nsplit s ~by:"\n"
let get_and_parse url =
let cmd = Format.sprintf "wget -q '%s' -O - | grep -o 'http\\(s\\)\\?://[^ \"]\\+'" url in
let content = Future.spawn_process ?stdin:None ~pool ~cmd in
content
|> Future.map (fun (_, stdout, _) -> stdout)
|> Future.map split_lines
|> Batteries.tap (fun lines ->
Future.on_success lines (fun lines -> Format.printf "downloaded %s (%d urls)@." url (List.length lines)))
type page = string * (string list Future.t)
(** The web graph; its vertices are annotated by futures of the content *)
let g : (page, string, unit) LazyGraph.t =
let force (url, future) =
Format.printf "force %s@." url;
let urls =
try Future.get future |> List.map (fun url -> (), (url, get_and_parse url))
with e -> [] in
let edges = Gen.of_list urls in
(* need to parse the page to get the urls *)
LazyGraph.Node ((url, future), url, edges)
in LazyGraph.make
~eq:(fun (url1,_) (url2,_) -> url1 = url2)
~hash:(fun (url,_) -> Hashtbl.hash url)
force
let pp_path fmt path =
List.print ~sep:"\n"
(fun fmt ((u1,_), (), (u2,_)) ->
String.print fmt u1; String.print fmt " -> "; String.print fmt u2)
fmt path
(* seek a path from the first url to the second *)
let path_between from into =
Format.printf "seek path from %s to %s@." from into;
let on_explore (url,_) = Format.printf " explore %s...@." url in
try
let cost, path = LazyGraph.dijkstra ~on_explore g
(from, get_and_parse from) (into, get_and_parse into) in
Printf.printf "found path (cost %f):\n%a\n" cost pp_path path
with Not_found ->
Format.printf "no path could be found@."
let print_limit file start depth =
Format.printf "print into %s webgraph starting from %s, up to depth %d@."
file start depth;
let start = start, get_and_parse start in
let g' = LazyGraph.limit_depth g depth (Gen.singleton start) in
let g'' = LazyGraph.map ~vertices:(fun v -> [`Label v]) ~edges:(fun _ -> []) g' in
let out = Format.formatter_of_out_channel (open_out file) in
LazyGraph.Dot.pp ~name:"web" g'' out (Gen.singleton start);
Format.pp_print_flush out ();
()
let _ =
let timer = Future.Timer.create () in
let rec ping () =
Format.printf "*** ping! (size of pool: %d)@." (Future.Pool.size pool);
Future.Timer.schedule_in timer 10. ping
in ping ()
let print_usage () =
Format.printf "usage: crawl path url1 url2@.";
Format.printf "usage: crawl print file url depth@.";
()
let _ =
match Sys.argv with
| [|_; "print"; file; url; depth|] ->
print_limit file url (int_of_string depth)
| [|_; "path"; from; into|] ->
path_between from into
| _ ->
print_usage ()

49
examples/dune Normal file
View file

@ -0,0 +1,49 @@
(executables
(names id_sexp ccparse_sexp ccparse_irclogs)
(libraries containers)
(flags :standard -warn-error -a+8))
(rule
(alias runtest)
(locks /ctest)
(deps
(source_tree test_data))
(action
(ignore-stdout
(run ./id_sexp.exe test_data/benchpress.sexp))))
(rule
(alias runtest)
(locks /ctest)
(deps
(source_tree test_data))
(action
(ignore-stdout
(run ./ccparse_sexp.exe test_data/benchpress.sexp))))
(rule
(targets ccparse_irclogs.ml)
(enabled_if
(>= %{ocaml_version} "4.08"))
(action
(copy ccparse_irclogs_real.cond.ml %{targets})))
(rule
(targets ccparse_irclogs.ml)
(enabled_if
(< %{ocaml_version} "4.08"))
(action
(with-stdout-to
%{targets}
(run echo "let() = print_endline {|ok|}"))))
(rule
(alias runtest)
(locks /ctest)
(deps
(source_tree test_data))
(enabled_if
(>= %{ocaml_version} "4.08"))
(action
(ignore-stdout
(run ./ccparse_irclogs.exe test_data/irc-logs.txt))))

View file

@ -1,20 +1,14 @@
let pp_sexp s =
open Result match s with
| Ok l -> List.iter (fun s -> Format.printf "@[%a@]@." CCSexp.pp s) l
let pp_sexp s = match s with | Error msg -> Format.printf "error: %s@." msg
| Ok l ->
List.iter
(fun s -> Format.printf "@[%a@]@." CCSexp.pp s)
l
| Error msg ->
Format.printf "error: %s@." msg
let () = let () =
match Sys.argv with match Sys.argv with
| [| _ |] -> | [| _ |] ->
let s = CCSexp.parse_chan_list stdin in let s = CCSexp.parse_chan_list stdin in
pp_sexp s pp_sexp s
| [| _; file |] -> | [| _; file |] ->
let s = CCSexp.parse_file_list file in let s = CCSexp.parse_file_list file in
pp_sexp s pp_sexp s
| _ -> failwith "usage: id_sexp [file]" | _ -> failwith "usage: id_sexp [file]"

View file

@ -1,112 +0,0 @@
(** Example of printing trees: lambda-term evaluation *)
type term =
| Lambda of string * term
| App of term * term
| Var of string
let _gensym =
let r = ref 0 in
fun () ->
let s = Printf.sprintf "x%d" !r in
incr r;
s
module SSet = Set.Make(String)
module SMap = Map.Make(String)
let rec fvars t = match t with
| Var s -> SSet.singleton s
| Lambda (v,t') ->
let set' = fvars t' in
SSet.remove v set'
| App (t1, t2) -> SSet.union (fvars t1) (fvars t2)
(* replace [var] with the term [by] *)
let rec replace t ~var ~by = match t with
| Var s -> if s=var then by else t
| App (t1,t2) -> App (replace t1 ~var ~by, replace t2 ~var ~by)
| Lambda (v, t') when v=var -> t (* no risk *)
| Lambda (v, t') -> Lambda (v, replace t' ~var ~by)
(* rename [t] so that [var] doesn't occur in it *)
let rename ~var t =
if SSet.mem var (fvars t)
then replace t ~var ~by:(Var (_gensym ()))
else t
let (>>=) o f = match o with
| None -> None
| Some x -> f x
let rec one_step t = match t with
| App (Lambda (var, t1), t2) ->
let t2' = rename ~var t2 in
Some (replace t1 ~var ~by:t2')
| App (t1, t2) ->
begin match one_step t1 with
| None ->
one_step t2 >>= fun t2' ->
Some (App (t1,t2'))
| Some t1' ->
Some (App (t1',t2))
end
| Var _ -> None
| Lambda (v,t') ->
one_step t' >>= fun t'' ->
Some (Lambda (v, t''))
let normal_form t =
let rec aux acc t = match one_step t with
| None -> List.rev (t::acc)
| Some t' -> aux (t::acc) t'
in
aux [] t
let _split_fuel f =
assert (f>=2);
if f=2 then 1,1
else
let x = 1+Random.int (f-1) in
f-x, x
let _random_var () =
let v = [| "x"; "y"; "z"; "u"; "w" |] in
v.(Random.int (Array.length v))
let _choose_var ~vars = match vars with
| [] -> Var (_random_var ())
| _::_ ->
let i = Random.int (List.length vars) in
List.nth vars i
let rec _random_term fuel vars =
match Random.int 2 with
| _ when fuel = 1 -> _choose_var ~vars
| 0 ->
let f1,f2 = _split_fuel fuel in
App (_random_term f1 vars, _random_term f2 vars)
| 1 ->
let v = _random_var () in
Lambda (v, _random_term (fuel-1) (Var v::vars))
| _ -> assert false
let print_term t =
PrintBox.mk_tree
(function
| Var v -> PrintBox.line v, []
| App (t1,t2) -> PrintBox.line "app", [t1;t2]
| Lambda (v,t') -> PrintBox.line "lambda", [Var v; t']
) t
let print_reduction t =
let l = normal_form t in
let l = List.map (fun t -> PrintBox.pad (print_term t)) l in
PrintBox.vlist ~bars:false l
let () =
Random.self_init ();
let t = _random_term (5 + Random.int 20) [] in
PrintBox.output ~indent:2 stdout (print_reduction t)

View file

@ -1,67 +0,0 @@
(** Compute the memory footprint of a value (and its subvalues). Reference is
http://rwmj.wordpress.com/2009/08/05/ocaml-internals-part-2-strings-and-other-types/ *)
(** A graph vertex is an Obj.t value *)
let graph =
let force x =
if Obj.is_block x
then
let children = Sequence.map (fun i -> i, Obj.field x i) (0--(Obj.size x - 1)) in
LazyGraph.Node (x, Obj.tag x, children)
else
LazyGraph.Node (x, Obj.obj x, Sequence.empty)
in LazyGraph.make ~eq:(==) force
let word_size = Sys.word_size / 8
let size x =
if Obj.is_block x
then (1+Obj.size x) * word_size
else word_size
let compute_size x =
let o = Obj.repr x in
let vertices = LazyGraph.bfs graph o in
Sequence.fold (fun sum (o',_,_) -> size o' + sum) 0 vertices
let print_val fmt x =
let o = Obj.repr x in
let graph' = LazyGraph.map ~edges:(fun i -> [`Label (string_of_int i)])
~vertices:(fun v -> [`Label (string_of_int v); `Shape "box"]) graph in
LazyGraph.Dot.pp ~name:"value" graph' fmt (Sequence.singleton o)
let print_val_file filename x =
let out = open_out filename in
let fmt = Format.formatter_of_out_channel out in
print_val fmt x;
Format.pp_print_flush fmt ();
close_out out
let process_val ~name x =
print_val_file (Format.sprintf "/tmp/%s.dot" name) x;
Format.printf "size of val is %d@." (compute_size x)
module ISet = Set.Make(struct type t = int let compare = compare end)
let mk_circ n =
let start = Sequence.to_list (1--n) in
(* make the end of the list point to its beginning *)
let rec cycle l = match l with
| [] -> assert false
| [_] -> Obj.set_field (Obj.repr l) 1 (Obj.repr start)
| _::l' -> cycle l'
in
cycle start;
start
let _ =
let s = Sequence.fold (fun s x -> ISet.add x s) ISet.empty (1--100) in
process_val ~name:"foo" s;
let l = Sequence.to_list (Sequence.map (fun i -> Sequence.to_list (i--(i+42)))
(Sequence.of_list [0;100;1000])) in
process_val ~name:"bar" l;
let l' = mk_circ 100 in
process_val ~name:"baaz" l';
()

View file

@ -0,0 +1,13 @@
(prover
(name msat)
(synopsis "msat for pure sat problems")
(version "git:.")
(sat "^Sat")
(unsat "^Unsat")
(cmd "$cur_dir/../msat.exe -time $timeout $file"))
(dir
(path $cur_dir)
(pattern ".*\\.cnf")
(expect (const unknown)))

View file

@ -0,0 +1,777 @@
2021-06-04 00:50:44 kluk> How do I start using DynArray from the ocaml command line?
2021-06-04 00:50:51 kluk> I have already done opam install extlib
2021-06-04 00:51:12 kluk> I am a newbie at OCaml
2021-06-04 05:18:03 dockerusocamlus> Hello! I'm minimizing an Alpine-based Docker image with OCaml installed via opam, and I'm trying to understand if I could erase some files to save some space. Basically, trying to understand if they are needed only on special situations, or if that would cause issues for users of the Docker image.
2021-06-04 05:19:46 dockerusocamlus> For instance, in this image, I have file ~/.opam/<version>/lib/ocaml/expunge, which take 15 MB of space. I don't think I have ever used it, but I don't know if it's internally used by some other OCaml process.
2021-06-04 05:28:12 dockerusocamlus> I don't have much documentation about it, and grepping ocaml's sources only shows a few occurrences. It seems related to the installation of the OCaml compiler itself, but even after removing it, I'm still able to do a `opam switch create` to install a different compiler, so... I guess it's fine to remove it?
2021-06-04 05:36:13 octachron> This is a compiler tool which is used to build REPLs. It is also used by utop.
2021-06-04 05:42:54 dockerusocamlus> Thanks!
2021-06-04 08:10:44 superherointj> Need some feedback on a minimalistic lwt demo: https://github.com/superherointj/lwt-demo1
2021-06-04 08:38:37 d_bot> <superherointj> Just solved it. I must be really tired.
2021-06-04 09:49:45 d_bot> <superherointj> Can anybody point me to a good article/information on incompatible ppx drivers (ppxlib and ocaml-migrate-parsetree)?
2021-06-04 09:49:46 d_bot> <superherointj> I have read already the saga blog post, but I am missing something.
2021-06-04 09:49:47 d_bot> <superherointj> I want to build my old project. I'm trying to replicate problem atm on a demo.
2021-06-04 09:50:25 companion_cube> people are supposed to use ppxlib, that's all I know
2021-06-04 09:51:25 d_bot> <superherointj> Any example?
2021-06-04 09:51:51 companion_cube> https://github.com/ocaml-ppx/ppx_deriving I guess?
2021-06-04 09:52:40 d_bot> <superherointj> Found this:
2021-06-04 09:52:41 d_bot> <superherointj> https://ppxlib.readthedocs.io/_/downloads/en/stable/pdf/
2021-06-04 09:57:49 d_bot> <EduardoRFS> Why does OCaml not optimizes this in a noop? Even under flambda and -O3
2021-06-04 09:57:49 d_bot> <EduardoRFS>
2021-06-04 09:57:51 d_bot> <EduardoRFS> ```ocaml
2021-06-04 09:57:52 d_bot> <EduardoRFS> let f (a, b) = (a, b)
2021-06-04 09:57:53 d_bot> <EduardoRFS> ```
2021-06-04 10:00:07 @adrien> it returns a new tuple, not the same one
2021-06-04 10:00:37 @adrien> let x = (1,2);; let f (a, b) = (a, b);; let y = f x;; y == x;;
2021-06-04 10:00:41 d_bot> <EduardoRFS> the question is why? It would change the `==` behavior but it's already not defined from what I remember
2021-06-04 10:01:06 d_bot> <EduardoRFS> it behaves differently in bytecode, native and IIRC it's also different in flambda
2021-06-04 10:01:14 companion_cube> I agree it'd be a valid optim
2021-06-04 10:02:19 d_bot> <EduardoRFS> This is especiall try for the case of different types and pattern matching but that generates identical data in memory, like
2021-06-04 10:02:20 d_bot> <EduardoRFS>
2021-06-04 10:02:21 d_bot> <EduardoRFS> ```ocaml
2021-06-04 10:02:22 d_bot> <EduardoRFS> type a = | A(int)
2021-06-04 10:02:24 d_bot> <EduardoRFS> type b = B(int)
2021-06-04 10:02:25 d_bot> <EduardoRFS> let f = function | A v -> B v
2021-06-04 10:02:26 d_bot> <EduardoRFS> ```
2021-06-04 10:02:36 @adrien> I get the same behaviour in native
2021-06-04 10:03:11 @adrien> and you can do f u = u
2021-06-04 10:03:13 companion_cube> @eduardors these are only the same by accident though
2021-06-04 10:03:18 companion_cube> seems far less useful as an optim
2021-06-04 10:03:18 zozozo> see https://github.com/ocaml/ocaml/pull/8958
2021-06-04 10:03:22 d_bot> <EduardoRFS> yes but the compiler knows it
2021-06-04 10:03:32 @adrien> not sure how is the generated code but in that case it's not a new tuple
2021-06-04 10:04:02 zozozo> there is a PR to do pretty much that (the link I posted above)
2021-06-04 10:04:05 theblatte> I keep writing functions like `let f ((a,b) as x0) = let a' = g a in let b' = g b in if a == a' && b == b' then x0 else (a', b')`
2021-06-04 10:04:07 d_bot> <EduardoRFS> in this case yes, but not all cases, I'm not asking about this specific tuple, I'm asking more about identical blocks that are known to be always identical
2021-06-04 10:04:13 companion_cube> I don't think it's a very useful optimization to see if per chance two different variants of different types happen to have the same binary representation
2021-06-04 10:04:33 companion_cube> more important stuff is to eliminate temporaries imho
2021-06-04 10:04:41 companion_cube> like a tuple built just to be deconstructed in the same function
2021-06-04 10:04:45 companion_cube> (or an option…)
2021-06-04 10:04:53 zozozo> companion_cube: what do you mean by "temporaries"?
2021-06-04 10:05:05 d_bot> <EduardoRFS> temporary allocations IIUC
2021-06-04 10:05:06 companion_cube> data that doesn't escape the current function :p
2021-06-04 10:05:09 zozozo> companion_cube: ah, well, avoiding these is more or less exactly the job of flambda, ^^
2021-06-04 10:05:11 companion_cube> (after inlining)
2021-06-04 10:05:12 companion_cube> yeah I know
2021-06-04 10:05:17 companion_cube> godspeed to you zozozo
2021-06-04 10:05:30 zozozo> ^^
2021-06-04 10:05:55 zozozo> @EduardoRFS : did you look at https://github.com/ocaml/ocaml/pull/8958 ?
2021-06-04 10:07:07 d_bot> <EduardoRFS> I'm looking on it, the argument of not being predictable is sad, it's a flat allocation reduction, no hidden allocation, not trying to make non efficient code efficient, but trying to make code that is efficient as possible more efficient
2021-06-04 10:07:34 zozozo> companion_cube: also, note that sometimes, because of type subtleties, you need to write the "identity" function, as a pattern match that then reconstructs exactly the same value, but with a slightly different type (thing GADTs), in such cases, being able to detect that a switch returns exactly its argument, is a nice improvements, and you can't really write it differently because of the types
2021-06-04 10:07:36 @adrien> well, as theblatte said, the "as" construct should help for that case
2021-06-04 10:08:04 d_bot> <EduardoRFS> that's exactly the case zozozo, for a lot of code in ocaml-migrate-types
2021-06-04 10:08:11 theblatte> zozozo: yes!
2021-06-04 10:08:19 zozozo> the advantage of the PR I linked is that it can trigger in cases where one cannot write code using "as"
2021-06-04 10:08:27 theblatte> much sad when that happens
2021-06-04 10:08:30 d_bot> <EduardoRFS> "as"?
2021-06-04 10:08:53 zozozo> @EduardoRFS : `let f ((a, b) as pair) = pair`
2021-06-04 10:09:13 d_bot> <EduardoRFS> oh but that works only for structural types
2021-06-04 10:09:21 companion_cube> zozozo: good argument against GADTs ;)
2021-06-04 10:09:34 d_bot> <EduardoRFS> companion_cube loves GADTs
2021-06-04 10:09:42 companion_cube> heh, in small doses
2021-06-04 10:09:47 theblatte> companion_cube: no need for GADTs! https://github.com/facebook/infer/blob/cfed4c4fa0c99ab1f42683bb92df76c8c8434e79/infer/src/pulse/PulseSummary.ml#L56
2021-06-04 10:10:03 olle> as?
2021-06-04 10:10:06 olle> new keyword?
2021-06-04 10:10:13 companion_cube> wait, theblatte, why
2021-06-04 10:10:14 theblatte> eg phantom type parameters
2021-06-04 10:10:18 companion_cube> ah yes
2021-06-04 10:10:29 companion_cube> it's unfortunate
2021-06-04 10:10:34 theblatte> (in my case not phantom but "phantom" because it doesn't show up in some of the variants)
2021-06-04 10:10:37 companion_cube> but it's the same constructors in this case.
2021-06-04 10:10:38 zozozo> companion_cube: gadts are useful *sometimes*
2021-06-04 10:10:40 d_bot> <EduardoRFS> I wonder if #8958 would be better as lambda layer
2021-06-04 10:10:44 d_bot> <EduardoRFS> but tempting to rebase it ;/
2021-06-04 10:11:25 zozozo> @EduardoRFS : the problem is that if you do that at lambda level, you miss out on situations where it happens after some inlining/simplification
2021-06-04 10:11:47 d_bot> <EduardoRFS> yeah but you ensure same behavior between all backends
2021-06-04 10:11:50 zozozo> (also, the code of lambda simplifications is quite a mess from what I hear)
2021-06-04 10:12:33 theblatte> companion_cube: same constructors: yes, personally I would only care about preserving physical equality when the objects are actually equal but ymmv
2021-06-04 10:12:34 zozozo> well.. there is now a pass specifically designed to implement optimizations, so why not use it ?
2021-06-04 10:13:05 theblatte> I've seen several examples where it would have a material effect on perf
2021-06-04 10:13:08 d_bot> <EduardoRFS> But the pass should not change behavior of code unless it provides a fallback, this is how I see most of it
2021-06-04 10:13:13 d_bot> <EduardoRFS> maybe Sys.opaque_identity would ignore it
2021-06-04 10:13:32 d_bot> <EduardoRFS> can we deprecate ==? That seems like a better idea overall
2021-06-04 10:13:34 companion_cube> zozozo: because it only works for native?
2021-06-04 10:13:41 companion_cube> ahahah
2021-06-04 10:13:47 companion_cube> removing == kills perf for other programs
2021-06-04 10:14:01 theblatte> #8958 ftw, I didn't know there'd been such a PR in flight for such a long time
2021-06-04 10:14:04 zozozo> companion_cube: well, bytecode is pretty much meant to not care about performance, so from that point of view it's not unreasonable
2021-06-04 10:14:05 d_bot> <EduardoRFS> not removing it, deprecating it, keep it under Obj.xx
2021-06-04 10:14:34 theblatte> == is an important part of the language, not an extension
2021-06-04 10:14:41 zozozo> the *good* solution would be to change the bytecode generation to use the result of flambda
2021-06-04 10:14:56 zozozo> the semantics of "==" is largely not officially specified
2021-06-04 10:14:56 d_bot> <EduardoRFS> NAH
2021-06-04 10:15:10 theblatte> but not a bad idea to not give it such an easily-confused name :p eg use "phys_equal" instead
2021-06-04 10:15:12 zozozo> and for any non-mutable record, there are next to no guarantees about "=="
2021-06-04 10:15:26 d_bot> <EduardoRFS> unless we had a blazing fast flambda pass, bytecode is so fast right now
2021-06-04 10:16:22 d_bot> <EduardoRFS> == is not exactly part of the language in many ways, and it's known to behave differently depending on the backend which should never happen for a specified feature of the language
2021-06-04 10:16:30 zozozo> @EduardoRFS: are you talking about compilation time or runtime of the compild program ?
2021-06-04 10:16:35 d_bot> <EduardoRFS> compilation time
2021-06-04 10:16:36 companion_cube> zozozo: I wish I could agree
2021-06-04 10:16:40 companion_cube> but some of us are stuck with bytecode
2021-06-04 10:16:45 d_bot> <EduardoRFS> bytecode is slow in runtime, really slow
2021-06-04 10:16:46 companion_cube> because that's the only official toplevel for now
2021-06-04 10:17:10 d_bot> <EduardoRFS> but bytecode generated from flambda would still work with the toplevel
2021-06-04 10:17:16 zozozo> companion_cube: yeah, but sometimes with others in my team, we talk about making it so that bytecode is generated after the flambda pass, which would solve all problems (if we can make it work)
2021-06-04 10:17:21 companion_cube> sure
2021-06-04 10:17:36 companion_cube> I mean in the future maybe we'll also have a JIT
2021-06-04 10:17:42 d_bot> <EduardoRFS> there is any plan on deprecating closure middle end?
2021-06-04 10:17:45 companion_cube> but for now it's not like there's a choice, and there's basically 0 optims on bytecode
2021-06-04 10:17:47 companion_cube> which… ugh
2021-06-04 10:19:26 d_bot> <dinosaure> it remmembers me one time when people compared ocsigenserver and http servers and used the bytecode version accidentally and say, OCaml is so bad
2021-06-04 10:19:34 companion_cube> :D
2021-06-04 10:19:38 d_bot> <EduardoRFS> D:
2021-06-04 10:19:49 companion_cube> or even using dune without --profile=release
2021-06-04 10:19:53 companion_cube> bye bye optims
2021-06-04 10:19:58 d_bot> <EduardoRFS> TEZOS IS RUNNING WITHOUT PROFILE=RELEASE
2021-06-04 10:20:25 d_bot> <EduardoRFS> even worse it is benchmarked without profile=release
2021-06-04 10:20:33 companion_cube> hu, weirder
2021-06-04 10:21:18 zozozo> well, if the switch is not using flambda, I don't think the difference is that important between the dev and release profiles
2021-06-04 10:22:34 companion_cube> err, you still have a bit of cross module inlining, don't you?
2021-06-04 10:22:39 companion_cube> with normal ocamlopt
2021-06-04 10:22:54 zozozo> I'm not sure
2021-06-04 10:22:55 d_bot> <EduardoRFS> yeah it makes difference, I benchmarked it, around 30% boost on some smart contracts
2021-06-04 10:23:06 d_bot> <EduardoRFS> dune without profile=release runs under -opaque
2021-06-04 10:23:10 companion_cube> I think it does, including for stuff like externals
2021-06-04 10:23:16 companion_cube> exactly
2021-06-04 10:23:25 companion_cube> --profile=release brings you back to normal behavior
2021-06-04 10:23:26 zozozo> I think (but I'm not sure) the only thing cross-inlined would be externals, but those are in the .mlis so no need for cross-optimization actually
2021-06-04 10:23:30 d_bot> <EduardoRFS> externals rely on the interface, so it doesn't depend on profile=release
2021-06-04 10:23:50 companion_cube> zozozo: but the .cmx ?
2021-06-04 10:24:00 theblatte> is profile=release different than passing -O3 to ocamlopt??
2021-06-04 10:24:05 zozozo> ah, maybe the small functions that closure unconditionally inline are inliend cross-modules by vanilla ocamlopt
2021-06-04 10:24:17 d_bot> <EduardoRFS> it is, because without profile=release you're under -opaque
2021-06-04 10:24:30 theblatte> whaaaat
2021-06-04 10:24:40 theblatte> :o
2021-06-04 10:24:44 d_bot> <EduardoRFS> that's the only way to achieve blazing fast build speed
2021-06-04 10:24:53 companion_cube> zozozo: the functions marked "inline" in .cmx files
2021-06-04 10:24:56 d_bot> <EduardoRFS> yup, small functions like having `Module.equal` are not inlined and Module.equal a lot of times is literally a single cnstruction
2021-06-04 10:25:09 theblatte> blazing fast = 6x slower than without -O3 ^^
2021-06-04 10:25:11 companion_cube> that's what I was talking about
2021-06-04 10:25:21 zozozo> companion_cube: indeed, ^^
2021-06-04 10:25:30 companion_cube> so it can make a big difference :)
2021-06-04 10:25:35 companion_cube> even without flambda
2021-06-04 10:25:45 theblatte> ohhh, recently-ish we noticed marking some functor arguments as [@inline] made a big difference
2021-06-04 10:25:52 companion_cube> :D
2021-06-04 10:25:59 zozozo> that's not surprising
2021-06-04 10:26:04 theblatte> is that sort of thing (adding @inline) not needed with flambda + release profile?
2021-06-04 10:26:25 theblatte> or is that independent?
2021-06-04 10:26:26 companion_cube> it still gives you better control
2021-06-04 10:26:34 zozozo> iirc, flambda tries as much as possibvle to inline functor applicaiton that are at toplevel, so you shouldn't need the annotations in that particular case
2021-06-04 10:26:51 companion_cube> do a lot of people use flambda1 in production?!
2021-06-04 10:26:59 zozozo> companion_cube: jane street i guess ?
2021-06-04 10:27:07 companion_cube> ahah maybe they have enough RAM
2021-06-04 10:27:16 zozozo> also, the binary release of dolmen is now compiled with flambda, :D
2021-06-04 10:27:18 companion_cube> I stopped using it years ago
2021-06-04 10:27:18 theblatte> infer is 30% faster with flambda, so you bet
2021-06-04 10:27:32 companion_cube> wow
2021-06-04 10:27:37 companion_cube> well can't wait for flambda2
2021-06-04 10:28:01 companion_cube> anyway, the point of --profile=release is to tell dune to not block optimizations, it doesn't enable new ones
2021-06-04 10:28:05 companion_cube> for that you can use ocamlopt_flags
2021-06-04 10:28:13 d_bot> <EduardoRFS> tezos is another 20% faster on flambda
2021-06-04 10:28:15 zozozo> we're trying very hard on making it so that flambda2 is as fast as possible, but it's hard sometimes
2021-06-04 10:28:27 companion_cube> zozozo: it's not just a question of "fast"
2021-06-04 10:28:35 companion_cube> it's also "not gobble up RAM on bad cases"
2021-06-04 10:28:38 theblatte> yes but I'm trying to understand if adding --profile=release will make a difference
2021-06-04 10:28:51 theblatte> I'll try that
2021-06-04 10:29:01 companion_cube> so, -p foo already switches to release mode
2021-06-04 10:29:12 companion_cube> it's only if you use `dune build @all` and that kind of stuff that it matters
2021-06-04 10:29:21 zozozo> companion_cube: right, can you send me (if you recall), the packages that were not working 'or taking ut too much RAM) ?
2021-06-04 10:29:24 companion_cube> it makes compilation slower (removes -opaque) but enables optimization
2021-06-04 10:29:27 companion_cube> zozozo: at least dose3
2021-06-04 10:29:30 companion_cube> that was the blocker
2021-06-04 10:29:32 zozozo> so that we can at least try and see what happens with flamdba2
2021-06-04 10:29:32 companion_cube> and camlp4
2021-06-04 10:29:35 d_bot> <EduardoRFS> even the new dose3?
2021-06-04 10:29:52 d_bot> <EduardoRFS> dose3 6 changed quite a bit of stuff, even parmap they're using now
2021-06-04 10:30:34 theblatte> companion_cube: we do "dune build infer.exe"
2021-06-04 10:31:29 companion_cube> lol
2021-06-04 10:31:39 companion_cube> yeah you need the flag
2021-06-04 10:31:54 companion_cube> idk about dose3 6
2021-06-04 10:32:01 companion_cube> I stopped trying flambda a while ago
2021-06-04 10:32:17 companion_cube> using too much ram is a big problem imho
2021-06-04 10:32:45 d_bot> <EduardoRFS> that seems weird, flambda reduces the number of allocations considerably
2021-06-04 10:33:30 companion_cube> per module
2021-06-04 10:33:38 companion_cube> with this you might also gain cross module
2021-06-04 10:33:54 theblatte> ah I thought you meant too much ram used during compilation :)
2021-06-04 10:34:09 companion_cube> that's what I meant yes
2021-06-04 10:34:11 companion_cube> sorry
2021-06-04 10:34:18 companion_cube> but theblatte, try the flag :p
2021-06-04 10:34:26 d_bot> <EduardoRFS> yeah makes sense
2021-06-04 10:34:29 theblatte> companion_cube: I am!!
2021-06-04 10:34:30 companion_cube> and also, make sure .cmx are installed for all libraries
2021-06-04 10:34:52 d_bot> <EduardoRFS> do we have an idea on what leads flambda to use so much memory?
2021-06-04 10:34:57 theblatte> companion_cube: how?
2021-06-04 10:35:14 companion_cube> well most should do it if they use dune
2021-06-04 10:35:25 d_bot> <ggole> Is there any info on flambda2 floating around yet?
2021-06-04 10:35:36 companion_cube> there's zozozo's brain
2021-06-04 10:35:40 companion_cube> although it's not floating
2021-06-04 10:39:04 d_bot> <dinosaure> technically, his brain is floating in his skull
2021-06-04 10:39:15 companion_cube> he might be a robot
2021-06-04 10:39:17 companion_cube> can't be sure
2021-06-04 10:39:27 d_bot> <EduardoRFS> if he is doing flambda2 he is a robot
2021-06-04 10:40:07 zozozo> right, I can try and answer questions about flambda2
2021-06-04 10:40:17 zozozo> since I'm working on it, ^^
2021-06-04 10:41:07 companion_cube> it'll be the default if it works well enough, right?
2021-06-04 10:41:53 zozozo> that's the plan
2021-06-04 10:43:01 companion_cube> 🤞
2021-06-04 10:43:57 d_bot> <ggole> Hmm, I'm not sure I know enough about it to ask good questions
2021-06-04 10:45:07 d_bot> <ggole> Although maybe "what was not adequate about the first flambda design" is an obvious one
2021-06-04 10:45:29 theblatte> companion_cube: ah, but actually we never use dune default profiles, we do --profile=opt (or dev). There's no -opaque in the build logs
2021-06-04 10:45:41 companion_cube> ah, I see
2021-06-04 10:45:47 theblatte> phew :)
2021-06-04 10:45:49 companion_cube> (wait, there's a profile=opt??)
2021-06-04 10:46:01 theblatte> you can name your profile however you want :p
2021-06-04 10:46:40 zozozo> @ggole: basically, flambda2 now uses a CPS representation of source code, which is very useful (whereas flambda1 had an ANF representation iirc)
2021-06-04 10:46:40 theblatte> then we have (env (opt (ocamlopt_flags (:standard -O3))), etc.
2021-06-04 10:47:35 theblatte> maybe we should have -opaque for profile=dev though!
2021-06-04 10:47:52 d_bot> <EduardoRFS> wondering, when the optimization mentioned in 8958 may be triggered after inlining?
2021-06-04 10:48:19 d_bot> <EduardoRFS> It would be weird if flambda allocated two identical temporary blocks
2021-06-04 10:48:30 d_bot> <Drup> I also have a question on flambda 2.0
2021-06-04 10:48:37 d_bot> <ggole> @guigui CPS is an interesting direction. It used to be the IL style of choice, but seems to have gone right out of favour.
2021-06-04 10:49:04 zozozo> Drup: fire away, ^^
2021-06-04 10:49:07 d_bot> <Drup> Do you (the flambda team) intend to keep working on it instead of instantly decide to shoot the for moon and work on flambda 3.0 ?
2021-06-04 10:49:36 companion_cube> lolol
2021-06-04 10:49:39 companion_cube> I could say the same of ppx
2021-06-04 10:49:44 zozozo> Drup: the plan is to continue working on flambda2
2021-06-04 10:50:14 d_bot> <ggole> Although people who use ANF seem to have discovered the need for very continuation-like constructs with join points
2021-06-04 10:50:17 zozozo> basically, doing flambda1 gave the team (note that this was before I joined) some insights about how to do and not to do some things
2021-06-04 10:50:17 d_bot> <Drup> (you don't have to answer it, it's friday evening, and I know you don't really have a sway on this all that much)
2021-06-04 10:50:50 zozozo> Drup: indeed, but I'm right now in a conference call with Pierre so I can ask him, ^^
2021-06-04 10:51:02 d_bot> <Drup> Say hello from me :p
2021-06-04 10:51:22 zozozo> Drup: he says hello to you too
2021-06-04 10:52:18 theblatte> hi pchambart :)
2021-06-04 10:52:48 companion_cube> coucou to him
2021-06-04 10:52:58 d_bot> <Drup> but yeah, flambda in general is a bit moonshot infused sometimes. I understand why (it's much more fun to work on "The Perfect IR") but it's a bit infuriating.
2021-06-04 10:53:28 companion_cube> like multicore has been for a while, too
2021-06-04 10:53:31 companion_cube> or even opam 2.1
2021-06-04 10:53:36 companion_cube> seems like a common theme in OCaml :p
2021-06-04 10:53:37 theblatte> companion_cube: alright so something good still came out of that: compiling with -opaqe turns a 50s full build into a 40s one \o/ and I assume it's even better for incremental build?
2021-06-04 10:53:42 zozozo> yeah, but now with flambda2 we should have a good enough IR to do what we want and need
2021-06-04 10:54:11 companion_cube> theblatte: err it's faster builds, but slower code, yes
2021-06-04 10:54:12 d_bot> <Drup> let's hope so
2021-06-04 10:54:34 theblatte> companion_cube: it's for "dev" builds
2021-06-04 10:54:49 companion_cube> then yes
2021-06-04 10:55:07 companion_cube> with -opaque you have fully separate compilation
2021-06-04 10:55:24 theblatte> I was wondering why dune was doing so much work on incremental compilation ^^
2021-06-04 10:55:31 theblatte> thanks!
2021-06-04 10:56:35 d_bot> <Drup> (I though dune already added `-opaque` for dev builds)
2021-06-04 10:57:05 d_bot> <ggole> @guigui what was difficult before that's easy now?
2021-06-04 10:57:06 companion_cube> seems like theblatte has his own profiles
2021-06-04 10:57:37 companion_cube> zozozo: so in CPS, do you have 2 "kinds" of function calls? normal and continuations?
2021-06-04 10:57:42 companion_cube> to make sure there's no new closures?
2021-06-04 10:57:53 d_bot> <Drup> That doesn't seem very smart if those are less though-out than the normal ones :3
2021-06-04 10:57:56 theblatte> dune profiles have... weird defaults
2021-06-04 10:58:24 theblatte> fair enough :p
2021-06-04 10:59:06 zozozo> companion_cube: continuations in flambda2 are more along the lines of static jumps
2021-06-04 10:59:12 companion_cube> cool
2021-06-04 10:59:33 companion_cube> zozozo: please stop delaying the PR for ocaml.org
2021-06-04 10:59:33 companion_cube> plz
2021-06-04 11:00:48 zozozo> sorry, ^^
2021-06-04 11:00:57 companion_cube> why does a PR against a fracking website take a full week to be merged anyway
2021-06-04 11:01:29 zozozo> right, that's a problem
2021-06-04 11:02:22 companion_cube> if you want the website to go stale because no one opens a PR to update it, that's the best way to go
2021-06-04 11:02:38 octachron> companion_cube, because there is noone clearly responsible? My commit right is normally mostly for OCaml releases
2021-06-04 11:03:07 companion_cube> is Anil trying to do too many things? :p
2021-06-04 11:03:21 companion_cube> definitely not blaming you octachron
2021-06-04 11:04:36 companion_cube> just annoyed that this, which should have taken literally 5 minutes, is taking a week
2021-06-04 11:04:41 theblatte> interesting, -opaque seems to make no difference for incremental compilation, only for full compilation
2021-06-04 11:04:46 companion_cube> during which the information on the website is misleading
2021-06-04 11:05:14 companion_cube> theblatte: try modifying a file deep in the dep graph, but only the implementation, not the interface
2021-06-04 11:05:22 theblatte> that's what I tried
2021-06-04 11:05:36 companion_cube> hu
2021-06-04 11:06:25 theblatte> humm, there's a leftover -opaque in the logs, my experiment must have gone wrong, sorry, digging in further
2021-06-04 11:11:27 d_bot> <EduardoRFS> theblatte: also opaque allows to build strictly against cmi which leads to better parallelism if you're using mli well
2021-06-04 11:12:30 d_bot> <EduardoRFS> so opaque should definitely matter for incremental as without it you need to rebuilt the full tree if any module changes
2021-06-04 11:12:36 d_bot> <EduardoRFS> maybe dune doesn't have this implemented?
2021-06-04 11:12:48 d_bot> <EduardoRFS> @rgrinberg any idea here?
2021-06-04 11:13:00 theblatte> I think because we use the "dev" name for our profile -opaque was already being passed!
2021-06-04 11:13:48 theblatte> even though we override (flags ...)
2021-06-04 11:13:53 theblatte> but not ocamlopt_flags
2021-06-04 11:15:11 octachron> companion_cube, anyway my week ended 15 minutes ago, so the PR is merged.
2021-06-04 11:16:16 theblatte> and we still see a win for the full build by forcing -opaque because it passes it in a bunch of places where dune doesn't by default
2021-06-04 11:16:58 theblatte> looks like that's when building the entire libraries' .cmx
2021-06-04 11:17:21 @adrien> octachron: thanks :)
2021-06-04 11:17:46 theblatte> so, hmmm, *shrug*
2021-06-04 11:39:10 companion_cube> octachron: 😂 thank you
2021-06-04 11:43:12 companion_cube> and the website is updated already, nice
2021-06-04 11:46:07 companion_cube> "variant constructor unboxing" that's nice
2021-06-04 11:46:16 companion_cube> didn't we discuss it here recently?
2021-06-04 11:46:21 companion_cube> perhaps about bitvectors
2021-06-04 11:51:05 olle> oooooh
2021-06-04 13:58:46 zozozo> @ggole : sorry for the delay, basically, control flow manipulation is much easier in cps form, also inlining a function's body is tricky to do in ANF (and can be exponential in the worst case if you need to ensure the result if in strict ANF)
2021-06-04 13:59:23 companion_cube> coudl you post a snippet of a tiny CPS AST? :p
2021-06-04 13:59:39 companion_cube> sth where we could see let, application, and like a primitive like + ?
2021-06-04 13:59:44 zozozo> sure
2021-06-04 13:59:56 companion_cube> 👍
2021-06-04 14:00:08 companion_cube> I want to see how the continuations are represented
2021-06-04 14:07:32 zozozo> https://gist.github.com/Gbury/7a02a35cb4906914fa351183490f11b2
2021-06-04 14:07:44 zozozo> basically, a continuation is a (unique) integer
2021-06-04 14:08:05 zozozo> companion_cube: ^
2021-06-04 14:09:06 companion_cube> so, apply_cont is where you jump
2021-06-04 14:09:09 zozozo> yup
2021-06-04 14:09:29 zozozo> also, after a function call (i.e. Apply_expr), you call the given continuation with the return value of the function call
2021-06-04 14:09:35 companion_cube> and why is there 2 let?
2021-06-04 14:09:42 companion_cube> yeah
2021-06-04 14:09:49 companion_cube> and you call the function on already computed arguments
2021-06-04 14:09:59 zozozo> you can bind continuations, and regular expressions
2021-06-04 14:10:37 companion_cube> hmmm
2021-06-04 14:10:54 companion_cube> I mean, Let_expr makes sense, it's a local definition, ok
2021-06-04 14:11:00 companion_cube> but what's the "handler" in Let_cont?
2021-06-04 14:11:00 zozozo> yup
2021-06-04 14:11:07 zozozo> the code of the continuation
2021-06-04 14:11:17 companion_cube> oh shit ok
2021-06-04 14:11:17 zozozo> let_cont k args = handler in body
2021-06-04 14:11:22 companion_cube> nice
2021-06-04 14:11:43 zozozo> note that continuations are local to a function's body and cannot escape
2021-06-04 14:11:44 companion_cube> so patmatch could also create such expressions, for example
2021-06-04 14:11:55 zozozo> since continuations are not regular value (i.e. simples or named)
2021-06-04 14:11:55 companion_cube> with explicit sharing and everything
2021-06-04 14:12:02 zozozo> yes
2021-06-04 14:12:29 companion_cube> (I imagine switch could also have a default case)
2021-06-04 14:12:49 zozozo> in this case no, the switch has no default case
2021-06-04 14:12:56 zozozo> it simplifies some things
2021-06-04 14:13:07 zozozo> but in theory it could
2021-06-04 14:13:08 companion_cube> even in flambda2?
2021-06-04 14:13:17 companion_cube> I guess since you can share continuations, it's ok
2021-06-04 14:13:24 zozozo> it's just that having no default case means the code is much more regular
2021-06-04 14:13:29 zozozo> you can fold on the arms of the switch
2021-06-04 14:13:41 zozozo> and not have to specifically treat the default case
2021-06-04 14:15:30 companion_cube> heh, fair enough
2021-06-04 14:16:03 companion_cube> I think the insight that continuations are not values, is sth I didn't realize
2021-06-04 14:16:05 companion_cube> so thank you! :)
2021-06-04 14:16:27 zozozo> no problem, ^^
2021-06-04 14:30:12 d_bot> <ggole> zozozo: hmm, that's actually pretty close to what I expected. Thanks for taking the time to write it up.
2021-06-04 14:33:07 d_bot> <ggole> When I tried CPS ILs I found it difficult to perform what should be simple transformations like commuting `case` expressions, but perhaps my approach was too naive.
2021-06-04 14:37:04 zozozo> @ggole : well, commuting switches would be quite complicated indeed (and isn't done currently in flambda2)
2021-06-04 14:38:59 d_bot> <ggole> That's one benefit of a more lambda-calculus like IL, it's quite easy to do context-directed optimisations (of which commuting is probably the most significant)
2021-06-04 14:39:37 zozozo> yeah, but then again, I don't think commuting is really something that we want to do in flambda2
2021-06-04 14:39:39 d_bot> <ggole> But there are downsides with scope
2021-06-04 14:39:55 d_bot> <colin> will flambda2 carry through to faithful CPS compilation or what
2021-06-04 14:40:21 zozozo> @colin : I'm not sure what you mean ?
2021-06-04 14:41:00 d_bot> <ggole> SML/NJ style CPS all the way? Seems unlikely.
2021-06-04 14:41:03 d_bot> <colin> I've seen compilers that use CPS as an IR yet blast to something slightly different to compile to something that still uses a runtime stack
2021-06-04 14:41:22 d_bot> <colin> Yeah, I don't think SML/NJ or MLton can be described as using CPS to much of an extent nowadays tbh
2021-06-04 14:41:57 d_bot> <ggole> I thought SML/NJ still used that for their `Cont` implementation
2021-06-04 14:41:57 zozozo> ah well, the flambda IR is in CPS, but there will be no change to the other IR of the compiler, so that's that, ^^
2021-06-04 14:43:13 d_bot> <colin> is the Apply_cont constructor in this cps.ml file representing "contificated"/static continuations?
2021-06-04 14:43:43 zozozo> yeah, it represents static continuations bound previously by a Let_cont
2021-06-04 14:43:59 d_bot> <colin> interesting, I've only ever seen the IR presented in Appel's CwC book
2021-06-04 14:44:30 d_bot> <ggole> There's a nice paper on an CPS IR a bit like this that you might be interested in
2021-06-04 14:44:36 d_bot> <colin> is it by Kennedy
2021-06-04 14:44:42 d_bot> <ggole> Yeah
2021-06-04 14:44:56 d_bot> <colin> yeah, I've seen that as well actually, it's the one most people seem to go with I think
2021-06-04 14:45:17 d_bot> <ggole> Makes a lot of sense if you aren't supporting call/cc
2021-06-04 14:45:18 companion_cube> zozozo: what comes after flambda? something with a control flow graph already?
2021-06-04 14:45:36 zozozo> companion_cube: after flambda, it's cmm
2021-06-04 14:46:07 d_bot> <colin> been a while since I've toyed with CPSing compilers because very few go the full mile with the whole "no runtime stack" - they go the chicken route and use it as a GC nursery because they can't get their C compiler to do the strict (tail) call -> jumps that CPS requires and LLVM certainly can't handle CPS so you're just stuck writing your own back-end each time
2021-06-04 14:46:17 zozozo> (fun factoid: cmm quite literraly means C minus minus, :p )
2021-06-04 14:46:56 d_bot> <ggole> If the continuations are second class as in this example, then you can probably linearise to SSA fairly successfully
2021-06-04 14:47:25 companion_cube> hmm so cmm still has function calls and expressions, but no types, right?
2021-06-04 14:47:33 d_bot> <colin> I just think going from ANF -> LLVM (SSA) is simpler
2021-06-04 14:47:41 d_bot> <ggole> Although there's the usual complications of closure conversion and whatnot because LLVM is first order
2021-06-04 14:48:10 d_bot> <colin> Oleg seems to have some strong views on actually doing faithful compilation of CPS as well, along the lines of "whole-program continuations are never useful" and uh "the garbage collector doesn't like this" etc. paraphrasing (perhaps inaccurately) here
2021-06-04 14:48:21 zozozo> companion_cube: cmm has very minimal types (basically it says whether a value can/should be scanned)
2021-06-04 14:48:39 d_bot> <ggole> Well, CPS as a compiler IL is a different storly to exposing continuations reified as functions
2021-06-04 14:48:42 companion_cube> yeah, that's not typing ;)
2021-06-04 14:49:20 companion_cube> but there you eliminate continuations again, right? towards some sort of static jump, like local exceptions?
2021-06-04 14:49:27 zozozo> yup
2021-06-04 14:49:38 zozozo> cmm has static jumps and flambda continuations maps perfectly to that
2021-06-04 14:49:50 zozozo> (ofc continuations that are used exactly once can be inlined)
2021-06-04 14:50:23 companion_cube> right
2021-06-04 14:50:32 d_bot> <ggole> Either a return or a jump
2021-06-04 14:50:36 d_bot> <colin> this discussion is urging me to actually go and read Shivers' k-CFA stuff since I've always just avoided any real detail/proposed benefit of program transformations in CPS
2021-06-04 14:50:39 companion_cube> you can still use static jumps for patmathc and stuff
2021-06-04 14:50:54 d_bot> <ggole> Or maybe an exception handler if double-barrelled CPS
2021-06-04 14:51:18 zozozo> flambda actually has double-barrelled CPS
2021-06-04 14:51:22 zozozo> (flambda2)
2021-06-04 14:51:47 d_bot> <ggole> That makes sense, rather than duplicating all of the control constructs
2021-06-04 14:51:51 d_bot> <ggole> And optims on them
2021-06-04 14:52:40 d_bot> <colin> what's double-barrelled, just doing the CPS twice?
2021-06-04 14:52:58 companion_cube> wait
2021-06-04 14:53:03 companion_cube> does the second handler also work for effects?
2021-06-04 14:53:10 companion_cube> or wolud there be a third handler?
2021-06-04 14:53:11 d_bot> <ggole> Along with the usual return continuation you pass another continuation which is the error/exn path
2021-06-04 14:53:42 d_bot> <colin> ah
2021-06-04 14:54:19 zozozo> companion_cube: effects as in algebraic effects (cf multicore) ?
2021-06-04 14:54:29 companion_cube> yes
2021-06-04 14:54:34 companion_cube> runtime effects anyway
2021-06-04 14:54:38 companion_cube> the one shot continuations :)
2021-06-04 14:54:43 zozozo> that's a very good question
2021-06-04 14:55:21 companion_cube> I think exceptions will just be another effect, except in the type system, so you can probably only have 2
2021-06-04 14:55:22 d_bot> <colin> who funds OCamlPro? INRIA? Jane Street? or is it its own company
2021-06-04 14:57:27 d_bot> <Christophe> I have a question about the change log of 4.13. The change "type check x |> f and f @@ x as (f x) ` is marked as breaking change. What are the consequences of that change actually? (sorry for interrupting a very interesting conversation)
2021-06-04 14:59:15 companion_cube> it might change a few things in a subtle way
2021-06-04 14:59:22 companion_cube> like `f x` can be `f ?a ?b x`
2021-06-04 14:59:26 companion_cube> if f has optional arguments
2021-06-04 14:59:43 zozozo> @colin : OCamlPro is its own company, and janestreet is one client of ocamlpro
2021-06-04 15:00:51 d_bot> <colin> Ah, I see, I was looking at compiler jobs at Jane Street (wishful thinking) but now they don't seem like they'd be as interesting as this flambda2 stuff (unless there's some ties between both companies)
2021-06-04 15:01:19 d_bot> <Christophe> Ah yes, I didn't think of optional arguments, thanks!
2021-06-04 15:01:37 companion_cube> aren't they funding flambda2? :D
2021-06-04 15:01:37 zozozo> @colin : well, the work on flambda2 is funded by JaneStreet, ^^
2021-06-04 15:41:47 d_bot> <EduardoRFS> type check of `x |> f` as `f x` is something I was not expecting but I really appreciate
2021-06-04 15:42:00 d_bot> <EduardoRFS> now we need to type check `let x = y` in the opposite order
2021-06-04 15:43:25 d_bot> <EduardoRFS> can we implement this kind of subtyping or would it be unsound?
2021-06-04 15:43:26 d_bot> <EduardoRFS> ```ocaml
2021-06-04 15:43:27 d_bot> <EduardoRFS> module X : sig
2021-06-04 15:43:28 d_bot> <EduardoRFS> type 'a t = private 'a
2021-06-04 15:43:30 d_bot> <EduardoRFS> end = struct
2021-06-04 15:43:31 d_bot> <EduardoRFS> type 'a t = 'a
2021-06-04 15:43:32 d_bot> <EduardoRFS> end
2021-06-04 15:43:34 d_bot> <EduardoRFS> let add (a : int X.t) (b : int) = a + b
2021-06-04 15:43:35 d_bot> <EduardoRFS> ```
2021-06-04 16:03:27 d_bot> <octachron> This is already implemented, with an explicit coercion as usual: `let add a b = (a:int X.t:>int) + b`
2021-06-04 19:56:48 hackinghorn> hi
2021-06-04 19:57:03 hackinghorn> how do I run commands like ls for linux in ocaml?
2021-06-04 19:59:38 dh`> there's a binding for system() somewhere
2021-06-04 19:59:40 hackinghorn> oh, fileutils work
2021-06-04 19:59:56 hackinghorn> got it, thanks
2021-06-04 23:15:51 d_bot> <EduardoRFS> Why not implicit?
2021-06-04 23:20:48 companion_cube> There are no implicit coercions in ocaml
2021-06-04 23:51:53 d_bot> <dj charlie> 👀 nice to see the stdlib increasingly fleshed out feels good
2021-06-05 00:39:14 companion_cube> like what?
2021-06-05 00:57:05 d_bot> <dj charlie> like fold_left and fold_right with the strings
2021-06-05 00:57:12 d_bot> <dj charlie> the math functions for floats
2021-06-05 01:05:15 companion_cube> Lolol ok
2021-06-05 01:05:33 companion_cube> Fold on string, heh?
2021-06-05 01:05:43 companion_cube> Forgot that that wasn't there
2021-06-05 01:06:10 d_bot> <dj charlie> hey guy who wrote his own stdlib
2021-06-05 01:06:13 d_bot> <dj charlie> it's pretty cool to me ok?
2021-06-05 07:50:23 companion_cube> :D it is, it is
2021-06-05 09:57:02 tane> howdy! found the way
2021-06-05 11:46:29 d_bot> <giga_08> anyone familiar with ocaml verification? termination in particular
2021-06-05 12:03:08 d_bot> <darrenldl> small code or large projects?
2021-06-05 12:41:30 d_bot> <giga_08> small code
2021-06-05 13:02:29 companion_cube> @giga_08 you could give a look at try.imandra.ai (it's proprietary but termination checking is def. sth interesting)
2021-06-05 18:18:14 d_bot> <TheSkeward> learning ocaml and I occasionally giggle to myself because "O Caml! My Camel!" will pop into my head like a line from some sort of desert-themed walt whitman poem
2021-06-05 18:19:38 companion_cube> `my $camel` sounds more like perl, tbh
2021-06-05 18:21:20 d_bot> <TheSkeward> perls before swine
2021-06-05 23:22:45 kluk> how do I start using DynArray? I tried include DynArray, include Extlib, nothing works
2021-06-05 23:23:07 companion_cube> you need to have it in your dune file, if you use dune
2021-06-05 23:23:10 companion_cube> and to install it in the first place
2021-06-05 23:24:09 kluk> I don't know what dune is yet, I'm still a beginner at OCaml. how do I install DynArray? with opam right?
2021-06-05 23:24:51 companion_cube> hmmm if you're that beginner, maybe take a look at a book
2021-06-05 23:24:55 companion_cube> there's a lot to explain :/
2021-06-05 23:26:43 kluk> I just wanted to play around on the ocaml repl with some arrays... not looking for making a project, folders, dune stuff, any of that, if possible to avoid at this point. Is it possible to just play with the OCaml language to learn it and not worry about how it mixes up with unix?
2021-06-05 23:27:56 companion_cube> ah well, sure, just type `ocaml`
2021-06-05 23:28:07 companion_cube> but Dynarray is a 3rd party library for vectors/resizable arrays
2021-06-05 23:28:16 companion_cube> it's not exactly a central type in OCaml :
2021-06-05 23:28:17 companion_cube> :p
2021-06-05 23:29:14 kluk> yes I can get to the repl, but I wanted to play with arrays first without worrying about packages, does that make sense? I wanted to explore OCaml the language first, like a try.ocaml.org sort of thing if that makes sense... I wanted to have some fun with the language and learn it and not have to think about packages and managing projects for a little
2021-06-05 23:30:40 kluk> I need a stack whose elements can be randomly accessed by an integer so I just happen to have an exact use case for arrays, but I am open to suggestions
2021-06-05 23:34:07 companion_cube> arrays are in the stdlib
2021-06-05 23:34:17 companion_cube> not dynamic arrays
2021-06-05 23:34:31 companion_cube> but yeah, a stack with indexing is a good use case
2021-06-05 23:34:55 kluk> companion_cube :)
2021-06-06 00:03:27 d_bot> <Bluddy> IMO vectors should replace arrays as a primary data type in the language
2021-06-06 00:04:29 companion_cube> why "replace"?
2021-06-06 00:04:42 companion_cube> I think it'd be nice to be able to build them safely
2021-06-06 00:04:47 d_bot> <Bluddy> as the *primary* data type
2021-06-06 00:04:51 companion_cube> but otherwise, they have some overhead
2021-06-06 00:05:03 companion_cube> arrays are simpler as they're always fully initialized
2021-06-06 00:05:11 d_bot> <Bluddy> yeah the overhead is very minor though
2021-06-06 00:05:34 d_bot> <Bluddy> very few languages have arrays as their primary data structure
2021-06-06 00:05:48 d_bot> <Bluddy> python's lists are vectors
2021-06-06 00:05:49 companion_cube> I mean… java?
2021-06-06 00:06:12 companion_cube> I think the problem is the GC, because in a vector you need some unitialized space
2021-06-06 00:06:15 companion_cube> even in rust it's quite dirty
2021-06-06 00:06:46 d_bot> <Bluddy> hmm
2021-06-06 00:07:10 companion_cube> it's hard to do well without a bit of Obj currently :/
2021-06-06 00:08:53 d_bot> <Bluddy> ok so I guess python/ruby's bias may be due to their reference counting
2021-06-06 00:09:11 companion_cube> also they're insanely high level and slow :p
2021-06-06 00:09:39 d_bot> <Bluddy> yeah but that's beside the point. java has array, c# has array vs List (really a vector)
2021-06-06 00:09:54 companion_cube> java has ArrayList, but only for boxed types
2021-06-06 00:09:59 companion_cube> the primitive on the JVM is arrays, same as OCaml
2021-06-06 00:10:07 companion_cube> (except with unsound variance)
2021-06-06 00:10:12 d_bot> <Bluddy> right
2021-06-06 00:10:30 d_bot> <Bluddy> ok so yeah I think I'm just using python too much recently
2021-06-06 00:11:00 d_bot> <Bluddy> javascript also has array as its primary type
2021-06-06 00:11:07 companion_cube> remember that in OCaml, an array is *one* word of overhead
2021-06-06 00:11:12 d_bot> <Bluddy> so are python and ruby really the exceptions?
2021-06-06 00:11:26 companion_cube> as far as primitive types go? I'm not sure
2021-06-06 00:13:25 d_bot> <EduardoRFS> JS arrays are dynamic arrays / vectors
2021-06-06 00:13:34 d_bot> <EduardoRFS> and the implementation of it is really all over the place
2021-06-06 00:13:51 d_bot> <Bluddy> perl has dynamic arrays. also reference counted
2021-06-06 00:14:18 companion_cube> _scripting languages_ were primitives are all in C
2021-06-06 00:15:51 d_bot> <Bluddy> interesting. and it's gc'd.
2021-06-06 00:16:30 d_bot> <Bluddy> @companion_cube GC is only an issue if you don't have a bit to tell the GC not to scan the uninitialized memory. If OCaml had it, it wouldn't be an issue.
2021-06-06 00:16:58 companion_cube> sure, if you entirely rewrite the GC so it's not just based on the initial tag… :p
2021-06-06 00:17:13 d_bot> <EduardoRFS> but JS objects nowadays operates like OCaml blocks, adding and removing field is generally a bad idea because of the types, while it is possible that can trigger a whole lot of compiled and optimized code to be invalidated
2021-06-06 00:17:15 d_bot> <Bluddy> hmm.. no I guess you need to build it into the GC process itself so it knows how to process the vector
2021-06-06 00:17:24 d_bot> <Bluddy> so it looks at length vs capacity
2021-06-06 00:17:26 d_bot> <EduardoRFS> well we can extend the object header
2021-06-06 00:17:26 companion_cube> (well for a vector you'd need to fit 2 sizes in one, basically: capacity, and actual size)
2021-06-06 00:17:35 d_bot> <EduardoRFS> I'm looking on it during the shower
2021-06-06 00:17:52 d_bot> <Bluddy> yeah a bit is not enough, you need to teach the GC about a new kind of object
2021-06-06 00:18:00 companion_cube> also remember that vectors are 2 levels of indirection, not one
2021-06-06 00:18:06 companion_cube> one to the {len,capacity,ptr}
2021-06-06 00:18:12 companion_cube> + the pointer itself
2021-06-06 00:18:31 companion_cube> but you've got to have this level of indirection so you can change the underlying array/pointer
2021-06-06 00:19:02 d_bot> <Bluddy> that's true
2021-06-06 00:19:36 companion_cube> so that's non trivial overhead compared to a basic array, when all you need is an array
2021-06-06 00:19:53 d_bot> <EduardoRFS> but that access can be mostly reduced if you know the cell size at compile time
2021-06-06 00:19:56 d_bot> <Bluddy> the problem is that you very rarely need an array
2021-06-06 00:20:38 d_bot> <Bluddy> if your primary type is a list, all an array gives you is mutability + O(1) access to any element. it's good, but the lack of ability to extend it is annoying
2021-06-06 00:20:46 d_bot> <Bluddy> if you're doing mutable stuff, you almost always want to extend it
2021-06-06 00:20:56 companion_cube> idk, it's nice in ASTs for example
2021-06-06 00:21:03 companion_cube> I agree that often a vector is also useful
2021-06-06 00:22:19 d_bot> <EduardoRFS> I wonder if having an unrolled linked list with some tricks wouldn't be enough for almost all cases
2021-06-06 00:22:53 companion_cube> for mutable stuff we just should have a good vector
2021-06-06 00:22:59 d_bot> <EduardoRFS> like couple cells all cache aligned + pointers to additional cells if they were created all together so that you can do O(1) after a List.map
2021-06-06 00:23:03 companion_cube> for immutable stuff, we _could_ use HAMT… but well
2021-06-06 00:25:01 d_bot> <EduardoRFS> copy on write is the solution to all problems
2021-06-06 00:25:11 companion_cube> noooo :D
2021-06-06 00:27:33 d_bot> <EduardoRFS> computers are fun, nowadays you have an ALU and caching inside of the MMU
2021-06-06 00:28:05 d_bot> <EduardoRFS> lisp machine to rule them all
2021-06-06 00:51:48 d_bot> <Bluddy> companion_cube: what do you do to prevent the GC from scanning the uninitialized vector area?
2021-06-06 00:53:27 d_bot> <EduardoRFS> If it is set to 0x0 the GC should just behave normally, it's a block of tag 0, size 0
2021-06-06 00:57:50 companion_cube> @Bluddy in containers, indeed, I fill the vector with 0
2021-06-06 00:58:03 companion_cube> or 0.0 if it's a float array 🙄
2021-06-06 01:34:37 d_bot> <Bluddy> ugh yeah that's bad
2021-06-06 01:34:57 companion_cube> not like we have a better option, imhp
2021-06-06 01:34:59 companion_cube> imho
2021-06-06 01:37:39 d_bot> <Bluddy> I wonder what other languages do
2021-06-06 01:37:44 d_bot> <Bluddy> ones with GC
2021-06-06 01:40:49 companion_cube> well, java fills with null I imagine
2021-06-06 01:40:54 companion_cube> boxed primitives and all that
2021-06-06 01:41:03 companion_cube> D… probably does ugly stuff?
2021-06-06 01:41:10 companion_cube> Go has 0 values for all types, so that's easy
2021-06-06 01:41:31 companion_cube> and the scripting stuff has nil/None/whatever to fill the blanks
2021-06-06 01:42:17 d_bot> <Bluddy> at the Obj level it would be nice if you could have a contiguous array where the size is the length, and right after that you'd place a string header with the remaining size
2021-06-06 01:42:38 companion_cube> you'd have to move the header every time you push/pop? :/
2021-06-06 01:42:48 d_bot> <Bluddy> not a huge deal. same cache line
2021-06-06 01:43:07 companion_cube> ideally push should be as simple and inlineable as possible :p
2021-06-06 01:43:53 d_bot> <Bluddy> still pretty simple. copy header over, reduce string size
2021-06-06 01:44:34 companion_cube> + code path for possible resize… that's a lot more than just a normal push
2021-06-06 01:44:37 d_bot> <Bluddy> pop doesn't need to do anything because you can just zero data out at that point
2021-06-06 01:45:12 d_bot> <Bluddy> that code path is there regardless
2021-06-06 01:45:38 d_bot> <Bluddy> a multi-push function can be more efficient as it can do the header copy once
2021-06-06 01:45:59 companion_cube> pop still needs to copy the header back
2021-06-06 01:46:58 d_bot> <Bluddy> yeah I guess that's true. the only annoying thing about the header is the size counter
2021-06-06 01:47:20 companion_cube> I'd rather wish OCaml had a primitive for partially initialized arrays, and that's it
2021-06-06 01:47:22 d_bot> <Bluddy> but it should be doable with a couple of instructions
2021-06-06 01:47:43 d_bot> <Bluddy> well that's not going to happen anytime soon
2021-06-06 01:48:23 d_bot> <Bluddy> it can happen in the 64-bit runtime, but the 32-bit cannot handle it
2021-06-06 01:48:38 d_bot> <Bluddy> because you need that extra header space for the size
2021-06-06 01:48:39 companion_cube> not sure how that's related :p
2021-06-06 01:49:03 companion_cube> I just want an API for the array with a valid 0 inside
2021-06-06 01:49:16 companion_cube> that doesn't force me to Obj.magic to see if it's a float array or normal array
2021-06-06 01:49:16 d_bot> <Bluddy> valid 0?
2021-06-06 01:49:26 companion_cube> a valid object for this array
2021-06-06 01:49:42 companion_cube> a valid object for this array, _as seen by the GC_
2021-06-06 01:51:38 d_bot> <Bluddy> is this another wish? to deal more easily with float arrays? or is it related?
2021-06-06 01:51:58 companion_cube> it's related because it's the only reason I have to use Obj in containers :p
2021-06-06 01:52:04 companion_cube> (or one of the few, I can't remember)
2021-06-06 01:52:20 companion_cube> to be able to implement a vector
2021-06-06 01:52:39 d_bot> <Bluddy> but it doesn't deal with this particular issue
2021-06-06 01:52:47 d_bot> <Bluddy> I mean they're phasing out float arrays
2021-06-06 01:52:57 companion_cube> yeah that'll be nice
2021-06-06 01:53:16 companion_cube> without float arrays one could always fill the array with 0
2021-06-06 01:53:29 companion_cube> since the GC doesn't mind 0
2021-06-06 01:53:55 d_bot> <Bluddy> yeah I see that piece of code now
2021-06-06 01:54:12 d_bot> <Bluddy> let fill_with_junk_ (a:_ array) i len : unit =
2021-06-06 01:54:15 companion_cube> yep yep
2021-06-06 01:54:27 d_bot> <Bluddy> https://github.com/c-cube/ocaml-containers/blob/95e96fb5e12558fa5b1e907a8e315d8c859c23b8/src/core/CCVector.ml#L27
2021-06-06 01:54:29 companion_cube> always interested in better ideas
2021-06-06 02:04:20 d_bot> <ggole> For 64-bit machine zero (not OCaml zero) is fine for float arrays as well
2021-06-06 02:05:07 d_bot> <ggole> So you might be able to get away with coercing to `float array` and then filling with `0.0`
2021-06-06 02:05:26 d_bot> <ggole> However, the recent `FloatArray` stuff might kill that idea
2021-06-06 02:08:30 d_bot> <ggole> The no naked pointer changes might also be trouble
2021-06-06 03:32:21 d_bot> <aotmr> Hi everyone! I'm a 3rd-year CS student making personal explorations into programming languages with an emphasis on functional and concatenative languages, as well as metaprogramming and optimizing compilers.
2021-06-06 03:33:32 d_bot> <aotmr> I'm currently using OCaml to build a functional FORTH interpreter that I hope to shape into a general optimizing FORTH compiler
2021-06-06 03:33:49 d_bot> <aotmr> And right now I'm investigating to what extent I can express FORTH concepts in OCaml
2021-06-06 03:42:01 d_bot> <ggole> Hmm, they're pretty different
2021-06-06 03:43:21 d_bot> <ggole> OCaml code is very variable heavy, which seems to be at odds with the Forth philosophy of communicating between tiny bits with the stack
2021-06-06 03:43:38 d_bot> <aotmr> So, for example, inside my VM state is a list representing the current data stack.
2021-06-06 03:43:38 d_bot> <aotmr> ```ocaml
2021-06-06 03:43:40 d_bot> <aotmr> type state = {
2021-06-06 03:43:41 d_bot> <aotmr> ds : Int.t list;
2021-06-06 03:43:42 d_bot> <aotmr> (* ... *)
2021-06-06 03:43:44 d_bot> <aotmr> }
2021-06-06 03:43:45 d_bot> <aotmr> ```
2021-06-06 03:43:46 d_bot> <aotmr> Stack-based interpreters are excellent matches for programming languages with pattern matching facilities, as it turns out.
2021-06-06 03:44:15 d_bot> <aotmr> ```ocaml
2021-06-06 03:44:16 d_bot> <aotmr> type opcode =
2021-06-06 03:44:17 d_bot> <aotmr> | Lit of Int.t
2021-06-06 03:44:19 d_bot> <aotmr> | Add
2021-06-06 03:44:20 d_bot> <aotmr> | Dot
2021-06-06 03:44:21 d_bot> <aotmr> (* ... *)
2021-06-06 03:44:23 d_bot> <aotmr> ```
2021-06-06 03:44:41 d_bot> <aotmr> Let's define a small opcode set for our VM: push a literal to the stack, add the top two on the stack, and print the top on the stack (`Dot`)
2021-06-06 03:46:01 d_bot> <aotmr> Now, here's where OCaml's list matching becomes very elegant. Let's define a function, `execute`, that takes a state and an opcode and returns a new state that reflects having executed the opcode.
2021-06-06 03:46:01 d_bot> <aotmr> ```ocaml
2021-06-06 03:46:03 d_bot> <aotmr> let execute st = function
2021-06-06 03:46:04 d_bot> <aotmr> | Lit i -> { st with ds = i::st.ds }
2021-06-06 03:46:05 d_bot> <aotmr> | Add -> (* ... *)
2021-06-06 03:46:07 d_bot> <aotmr> | Dot -> (* ... *)
2021-06-06 03:46:08 d_bot> <aotmr> ```
2021-06-06 03:46:32 d_bot> <colin> awaiting the IRC users who'll ask you to read the channel description
2021-06-06 03:46:43 d_bot> <aotmr> Aw shit 🤦‍♂️
2021-06-06 03:46:49 d_bot> <colin> :p
2021-06-06 03:46:52 zozozo> @aotmr : code blocks from discord do not render great on the irc side of this channel, so it'd be best if you could use some paste website to link to code when there are more than a few lines, ^^
2021-06-06 03:46:59 d_bot> <aotmr> There it is
2021-06-06 03:47:08 zozozo> haha, XD
2021-06-06 03:47:32 d_bot> <aotmr> Well all that goes to say
2021-06-06 03:47:32 d_bot> <aotmr> You can express stack operations using pattern matching.
2021-06-06 03:48:43 d_bot> <colin> if you think that's cute, you'll like a similar idea in dependent typing where you can express stack changes (as a list) indexing the opcodes or something similar
2021-06-06 03:48:44 d_bot> <aotmr> For example, to swap the top two items on the stack, you'd use the record update syntax
2021-06-06 03:48:45 d_bot> <aotmr> `{ st with ds = match st.ds with a:🅱️:tl -> b:🅰️:tl | _ -> assert false }`
2021-06-06 03:48:46 d_bot> <aotmr> Last code block for the time being, I promise 😅
2021-06-06 03:49:17 d_bot> <aotmr> (And you can also use `let` matching, I've found, but I can't get ocaml to stop complaining even though I fully understand it'll crash if there aren't enough elements)
2021-06-06 03:49:30 d_bot> <aotmr> Oh, have a paper on that?
2021-06-06 03:49:54 d_bot> <aotmr> I'm wanting to see how high-level I can get with forth and still generate good code for small microprocessors--say, for NES and game boy dev
2021-06-06 03:50:06 d_bot> <colin> no, just thought it was very cute when I studied Agda at university, relevant construction of Hutton's razor can be found at https://github.com/fredrikNordvallForsberg/CS410-20/blob/master/Coursework/Two.agda#L492-L506 what you're saying just reminded me of it, not really relevant just in case you wanted to see cute things
2021-06-06 03:50:15 zozozo> @aotmr : small one-line blocks of code (like your last one) are mostly okay I'd say, ^^
2021-06-06 03:50:48 d_bot> <aotmr> Oh I'll look at it never the less, thanks.
2021-06-06 03:50:49 d_bot> <aotmr> Forth has its own concept of combinators and I want to try to compile those efficiently
2021-06-06 03:52:04 d_bot> <aotmr> Honestly I'd say OCaml is distantly related to FORTH just usagewise, there's a similar concept of "pipelining". Where in FORTH you'd write a series of words, passing state between them implicitly on the stack, you do the same in Ocaml when expressing a `|>` or `@@` pipeline
2021-06-06 03:54:16 d_bot> <aotmr> This is an interesting idea as, while FORTH is typically untyped, I could use this concept to track the entire lifetimes of values throughout a program
2021-06-06 03:55:20 d_bot> <colin> it's just a nice encoding of how the stack ought to change, helps the type system help you implement it correctly (though not a full specification by any means, just a cute stack requirement)
2021-06-06 03:55:27 d_bot> <ggole> There are some interesting typed concatenative langs
2021-06-06 03:55:47 d_bot> <ggole> Kitten and Cat
2021-06-06 03:55:48 d_bot> <aotmr> I've finally taken the forth-pill so to speak because I finally understand how to implement a compiler for the language
2021-06-06 03:56:18 d_bot> <colin> a whole new world.mp3 https://llvm.moe/
2021-06-06 03:56:29 d_bot> <colin> see past stack-based paradigm
2021-06-06 03:56:58 d_bot> <aotmr> Well, once I have a compiler for a stack-based VM that opens the door to using it as an intermediate representation
2021-06-06 03:57:14 d_bot> <colin> would there be any benefit
2021-06-06 03:57:27 d_bot> <colin> I, admittedly, have never seen the appeal of stack-based languages for general programming
2021-06-06 03:57:32 d_bot> <colin> I used to write postscript by hand recreationally
2021-06-06 03:57:35 d_bot> <colin> but that's about it
2021-06-06 03:57:46 d_bot> <aotmr> It's admittedly kind of recreational
2021-06-06 03:58:10 d_bot> <aotmr> I think the real strength is in the way you can build an entire system from the ground up by hand and know every moving part
2021-06-06 03:59:32 d_bot> <aotmr> You could write an optimizing compiler x86 in, oh, a month
2021-06-06 04:00:51 d_bot> <colin> sadly the majority of back-end optimisations for x86 are really just suffering
2021-06-06 04:00:59 d_bot> <aotmr> OCaml's own VM is stack-based so it's kind of circular
2021-06-06 04:01:09 d_bot> <colin> yeah but that's just the bytecode OCaml stuff
2021-06-06 04:01:12 d_bot> <aotmr> Oh yeah no x86 is a horrible architecture to program for
2021-06-06 04:01:19 d_bot> <aotmr> Sure but it's still a neat thought
2021-06-06 04:01:25 d_bot> <aotmr> But I digress
2021-06-06 04:01:28 d_bot> <colin> I used to be confused as to why Xavier Leroy's earlier work seemed to focus rather specifically on bytecode stack machines as the target of Camls
2021-06-06 04:01:51 d_bot> <colin> but then someone said like "it was research into creating a tactic computational kernel for some proof assistant"
2021-06-06 04:02:01 d_bot> <colin> not sure how true that is, perhaps someone here can clarify if that's nonsense
2021-06-06 04:02:07 d_bot> <colin> and Xavier just really likes stack machines
2021-06-06 04:02:56 d_bot> <aotmr> So, it could be that you can take advantage of immutable VM states in unit testing
2021-06-06 04:03:13 d_bot> <aotmr> And using it to accelerate the general process
2021-06-06 04:04:16 d_bot> <aotmr> If you wanted to do an exhaustive search of the program P with inputs a, b, c..., you could run P over every possible value of a, b, c
2021-06-06 04:05:19 d_bot> <aotmr> That is, we're trying to find a, b, c... that causes P to fail
2021-06-06 04:06:00 d_bot> <ggole> There's actually some tooling for that
2021-06-06 04:06:02 d_bot> <ggole> See Crowbar
2021-06-06 04:06:08 d_bot> <aotmr> One way to speed up that process is to memoize the VM state, I think
2021-06-06 04:06:44 d_bot> <ggole> It's not exhaustive search, but coverage-feedback guided random generation
2021-06-06 04:06:47 d_bot> <aotmr> If we find a "success" set of (a, b, c...), we could maybe remember all of the previous states of the VM and if we ever encounter them again we can stop early
2021-06-06 04:07:14 d_bot> <aotmr> But that would blow up your space requirements for little speedup, I'd think
2021-06-06 04:07:17 d_bot> <colin> can see why that'd help (as a form of concolic execution) but I think the accepted reality in industry is that Google fuzz their own software over billions of instances using AFL on dozens of Google cloud instances and just consider that alright
2021-06-06 04:08:00 d_bot> <aotmr> My other use case is of a rewindable debugger where you can undo all the way back to the start of the program
2021-06-06 04:08:51 d_bot> <colin> time travel debugging is pretty cool
2021-06-06 04:09:07 d_bot> <aotmr> That also brings to mind the idea of a rewindable game engine, I think rewind mechanics are pretty cool in theory
2021-06-06 04:09:12 d_bot> <colin> I always wanted a clean injection mechanism for debugging
2021-06-06 04:09:27 d_bot> <colin> hot reloading debugging stubs, that kinda thing
2021-06-06 04:09:54 d_bot> <aotmr> I'm still not entirely familiar with the mechanics of debuggers
2021-06-06 04:10:07 d_bot> <colin> syscalls and suffering™
2021-06-06 04:10:36 d_bot> <aotmr> I'm under the impression that, if you can execute from RAM, you can at least single-step on pretty much any CPU
2021-06-06 04:11:58 d_bot> <colin> yeah there's architectural single step stuff provided by most systems; *nix has PTRACE_SINGLESTEP
2021-06-06 04:12:02 d_bot> <aotmr> If you want to single-step the instruction at a given address, then you'd write some kind of "breakpoint" opcode (or, crudely, even just an absolute jump) directly following it, but you'd have to know the length of the opcode beforehand
2021-06-06 04:12:27 d_bot> <aotmr> But I'd hope consumer CPUs can single-step in silicon by now 😅
2021-06-06 04:12:28 d_bot> <colin> variable length encoding is just one part of suffering in writing x86(_64) tooling, yes
2021-06-06 04:12:42 d_bot> <aotmr> Oh yeah I guess debugging has to be infinitely easier on a fixed-length RISC
2021-06-06 04:13:14 d_bot> <aotmr> Imagine if x86 had an instruction that only decoded the length of an instruction at a given address
2021-06-06 04:13:18 d_bot> <colin> I suppose there's other challenges, given the domain where RISC microprocessors are probably most prevalently being debugged
2021-06-06 04:13:39 d_bot> <colin> who knows, they might, Intel has a ton of hidden instructions and their manual doesn't even document some of them accurately
2021-06-06 04:13:46 d_bot> <aotmr> You're right, there probably is.
2021-06-06 04:14:06 d_bot> <ggole> There's tons of hardware support for debugging
2021-06-06 04:14:09 d_bot> <colin> it's common for trampoline hooking code to come with a "variable length decoder" as a form of minimal disassembler
2021-06-06 04:14:13 d_bot> <ggole> Watch registers and that kind of thing
2021-06-06 04:14:26 d_bot> <ggole> Pretty complicated from what I understand
2021-06-06 04:14:27 d_bot> <colin> to know how many bytes to replace w/ their placed `jmp` or `push ...; ret` etc.
2021-06-06 04:16:26 d_bot> <colin> but yeah, can't lie
2021-06-06 04:16:34 d_bot> <colin> confused how we went from stack langs to all this
2021-06-06 04:16:58 d_bot> <colin> what is your ambition, aotmr, to write a forth interpreter/compiler?
2021-06-06 04:19:34 d_bot> <aotmr> Just to do it, I guess. I think it's interesting to build a software stack nearly from the bottom up--or nearly so
2021-06-06 04:19:53 d_bot> <colin> what, in Forth?
2021-06-06 04:20:04 d_bot> <aotmr> I mean, build a Forth itself from the bottom up
2021-06-06 04:20:14 d_bot> <colin> oh alright
2021-06-06 04:20:29 d_bot> <aotmr> In theory it can even be possible to replace the Ocaml parts with Forth themselves
2021-06-06 04:21:15 d_bot> <aotmr> Though "bootstrapping"
2021-06-06 04:21:47 d_bot> <aotmr> First, I'd write a forth compiler in ocaml
2021-06-06 04:22:07 d_bot> <aotmr> Then, translate the compiler to forth
2021-06-06 04:22:17 d_bot> <aotmr> Compile the compiler-in-forth with the compiler-in-ocaml
2021-06-06 04:22:30 d_bot> <aotmr> And then I have a forth compiler, compiled and written in forth
2021-06-06 04:22:36 d_bot> <colin> can graduate to something hacky like JITing the FORTH then using C FFI to map the code and somehow return opaque caml values back to the user as callables within OCaml
2021-06-06 04:22:55 d_bot> <colin> galaxy brain interplay
2021-06-06 04:23:14 d_bot> <aotmr> That sounds terrifying
2021-06-06 04:23:22 d_bot> <colin> -ly based
2021-06-06 04:23:28 d_bot> <aotmr> You got it
2021-06-06 04:23:44 d_bot> <colin> don't actually know if you can do that
2021-06-06 04:23:52 d_bot> <colin> on the conceptual level, you certainly can with enough hacks
2021-06-06 04:24:21 d_bot> <aotmr> Probably the easiest way to "JIT" stack code is just to apply peephole optimization
2021-06-06 04:24:34 d_bot> <colin> can't lie, I hate stacks
2021-06-06 04:24:56 d_bot> <aotmr> The compiler writer writes manual superwords that implement a series of smaller words in a faster way
2021-06-06 04:26:26 d_bot> <aotmr> For example, replacing `>r + r>` with the much shorter machine code for the equivalent sequence that just adds the top element of the stack to the third
2021-06-06 04:42:07 d_bot> <BobbyT> Im just marinating in all these high level ideas
2021-06-06 05:58:42 ralu> I am trying to build infer, but I keep getting error about failed dune build. So i can not build dune. Has anyone has any pointers?
2021-06-06 09:38:22 d_bot> <Bluddy> What if we make it so a proper null pointer inside an array means the end of GC scanning?
2021-06-06 10:32:24 d_bot> <Drup> @Bluddy that's not compatible with a bunch of much more interesting representations improvements (like democratizing the Zarith hack, for instance)
2021-06-06 10:52:39 d_bot> <Deadrat> Would lightweight higher kinded types be added to ocaml in the future?
2021-06-06 10:58:32 d_bot> <xvw> With modular immlicits I guess that lightweight higher kinded types will be less useful
2021-06-06 11:08:02 d_bot> <rbrott> There's a nice chapter on that idea in CPDT: <http://adam.chlipala.net/cpdt/html/Cpdt.StackMachine.html>
2021-06-06 11:08:04 d_bot> <Bluddy> @Drup could you explain the 'zarith hack'?
2021-06-06 11:09:03 d_bot> <Deadrat> But they are still years away as I understand?
2021-06-06 11:09:35 d_bot> <Drup> @Bluddy A value of type `Z.t` in zarith is either a normal ocaml integer (63bits usually, etc) or a GMP "big integers"
2021-06-06 11:11:56 d_bot> <Drup> This is achieved by considering the type morally as `int | Big of gmp`. OCaml integers already have a bit put aside for the GC to differentiate them from pointers, so we don't need an extra tag to differentiate between small integers and pointers to a big integer.
2021-06-06 11:12:15 d_bot> <Drup> This is only possible by going through the C FFI
2021-06-06 11:12:29 d_bot> <ggole> Machine zero isn't an `int` or a block though
2021-06-06 11:15:09 d_bot> <Drup> @ggole I can never remember if the tag for integers is 0 or 1.
2021-06-06 11:17:58 d_bot> <ggole> It's 1
2021-06-06 11:18:24 d_bot> <ggole> But even if it were zero, you could set aside a non-valid pointer value to indicate a truncation spot
2021-06-06 11:20:59 d_bot> <Drup> right, I'm not sure how much I like it, but it could work
2021-06-06 11:26:30 d_bot> <ggole> I guess there would have to be an `Array.unsafe_set_terminator` or something, which would be a bit nasty
2021-06-06 11:26:41 d_bot> <ggole> And I dunno what the interaction with bounds checking would be
2021-06-06 11:27:07 d_bot> <ggole> I suspect they would be more trouble than the terminator value itself though
2021-06-06 11:49:23 d_bot> <Bluddy> I need to try it out and see the performance difference.
2021-06-06 11:51:38 d_bot> <Bluddy> it's not automatically clear that setting all the memory is a bad idea
2021-06-06 13:00:48 companion_cube> I'd just like to point out that no one else uses a terminator for vectors, afaik
2021-06-06 13:00:55 companion_cube> it seems like a pretty bad idea :p
2021-06-06 13:05:10 d_bot> <ggole> Most of the other langs with vectors can handle uninitialised memory or keep the bits there without leaks
2021-06-06 13:06:34 companion_cube> and again, it's not that common
2021-06-06 13:06:57 companion_cube> languages that compile to native and have a GC and don't rely on C to implement a ton of datastructures are not plenty
2021-06-06 13:47:15 d_bot> <aotmr> I'm still not entirely used to building data structures in any language *but* C, to be honest--it feels strange
2021-06-06 13:47:52 d_bot> <aotmr> I probably just don't have practice because C is the only language that I use that doesn't have a dynamic array, really
2021-06-06 13:48:49 companion_cube> well OCaml is excellent for implementing a lot of data structures
2021-06-06 13:49:01 companion_cube> vectors just happen to be a bit on the low-level, unsafe memory thingie side
2021-06-06 13:51:37 d_bot> <aotmr> What's a good way to map from a discriminated union to successive integers?
2021-06-06 13:51:43 d_bot> <aotmr> And the other way around?
2021-06-06 13:53:53 companion_cube> ppx_deriving.enum maybe?
2021-06-06 13:54:02 companion_cube> if it's an enum, without payload on the variants, that is.
2021-06-06 13:57:14 d_bot> <aotmr> Hmm
2021-06-06 13:57:14 d_bot> <aotmr> Here's a simpler question: how do I get the "tag" of a sum type?
2021-06-06 13:57:41 companion_cube> you don't :)
2021-06-06 13:57:45 d_bot> <aotmr> I figure I can quickly map integers to most of the opcodes and then manually handle opcodes with a payload
2021-06-06 13:57:47 companion_cube> it's not really specified in the language.
2021-06-06 13:57:48 d_bot> <aotmr> Oh...
2021-06-06 13:57:52 d_bot> <octachron> The simpler and most forward-compatible way is to write the function.
2021-06-06 13:58:11 d_bot> <aotmr> True, but then I'd have to write two functions and keep them in sync manually, or generate the code.
2021-06-06 13:58:35 d_bot> <aotmr> *sigh* Okay then
2021-06-06 13:58:37 companion_cube> the function from integers to variants seems impossible to write
2021-06-06 13:58:45 companion_cube> if they have payloads that is
2021-06-06 13:58:56 d_bot> <aotmr> I'd be converting from a packed representation
2021-06-06 14:01:41 companion_cube> your best chance is codegen indeed
2021-06-06 14:01:53 companion_cube> variant to int: generate a pattern matching function
2021-06-06 14:02:10 companion_cube> int+payload to variant: well, match on the int I guess
2021-06-06 14:04:58 d_bot> <aotmr> Actually wait, I'm wrong
2021-06-06 14:04:58 d_bot> <aotmr> I shouldn't have written the VM with a discriminated union like this anyways
2021-06-06 14:05:13 d_bot> <aotmr> But, I guess I might as well keep a separate encoded and decoded form
2021-06-06 14:10:07 companion_cube> a VM seems like a good use case for C or C++ or rust, ironically
2021-06-06 14:23:33 d_bot> <aotmr> Oh it's definitely more appropriate, but I'm actually making some headway
2021-06-06 14:24:11 d_bot> <aotmr> I haven't played with ocaml in quite some time (OS issues--it didn't work well on Windows for me until quite recently)
2021-06-06 14:24:23 companion_cube> glad to hear it works better now
2021-06-06 14:24:45 d_bot> <aotmr> I mean, it works better now because it's running in WSL 😆
2021-06-06 14:25:44 d_bot> <aotmr> So I'm happy that I remember how to build list to list mappings that produce and consume varying numbers of elements
2021-06-06 15:08:24 d_bot> <aotmr> Cool, so I've figured out how to build an encoder and decoder for a variable-length instruction stream
2021-06-06 18:00:25 kluk> I get "Error: Unbound module Batteries" after doing open Batteries;; on the ocaml repl after having done opam install batteries. what am I missing?
2021-06-06 18:04:03 companion_cube> #require "batteries";;
2021-06-06 18:04:12 companion_cube> (and possibly, before that, #use "topfind";;)
2021-06-06 18:07:13 kluk> Ahhh.. it wasn't clear to me that #use was needed to bring #require but now that I ran it I can see in its blurb that it does do that. Thank you very much.
2021-06-06 18:07:49 companion_cube> also note that if you use `utop` it does the topfind thing directly
2021-06-06 18:08:03 companion_cube> you can also put the blurb in ~/.ocamlinit
2021-06-06 18:11:31 kluk> companion_cube thank you for the .ocamlinit tip
2021-06-06 18:27:10 kluk> companion_cube so now I can use DynArray from Batteries just fine :) thanks so much for the help once again.
2021-06-06 18:35:30 companion_cube> heh

View file

@ -0,0 +1,17 @@
let gen_sexp =
let open! Crowbar in
let ( >|= ) = map in
fix (fun self ->
choose
[
(([ bytes ] : _ gens) >|= fun s -> `Atom s);
([ list self ] >|= fun l -> `List l);
])
let () =
Crowbar.add_test ~name:"ccsexp_csexp_reparse" [ gen_sexp ] (fun s ->
let str = CCCanonical_sexp.to_string s in
match CCCanonical_sexp.parse_string_list str with
| Ok [ s2 ] -> assert (s = s2)
| Ok _ -> failwith "wrong number of sexps"
| Error e -> failwith e)

View file

@ -0,0 +1,3 @@
let () =
Crowbar.add_test ~name:"ccsexp_parse_string_does_not_crash" [ Crowbar.bytes ]
(fun s -> CCSexp.parse_string s |> ignore)

View file

@ -0,0 +1,148 @@
let simple_uchar_to_string (c : Uchar.t) : string =
let c = Uchar.to_int c in
let bits =
Array.make 64 false
|> Array.mapi (fun i _ -> Int.shift_right c (63 - i) land 0x1 <> 0)
in
let char_of_bit_list bits =
let bits = Array.of_list bits in
assert (Array.length bits = 8);
let res = ref 0 in
for i = 0 to 7 do
if bits.(i) then res := !res lor (0x1 lsl (7 - i))
done;
Char.chr !res
in
let get_start_from_right i = Array.get bits (63 - i) in
let chars =
if c <= 0x7F then
[
[
false;
get_start_from_right 6;
get_start_from_right 5;
get_start_from_right 4;
get_start_from_right 3;
get_start_from_right 2;
get_start_from_right 1;
get_start_from_right 0;
];
]
else if c <= 0x7FF then
[
[
true;
true;
false;
get_start_from_right 10;
get_start_from_right 9;
get_start_from_right 8;
get_start_from_right 7;
get_start_from_right 6;
];
[
true;
false;
get_start_from_right 5;
get_start_from_right 4;
get_start_from_right 3;
get_start_from_right 2;
get_start_from_right 1;
get_start_from_right 0;
];
]
else if c <= 0xFFFF then
[
[
true;
true;
true;
false;
get_start_from_right 15;
get_start_from_right 14;
get_start_from_right 13;
get_start_from_right 12;
];
[
true;
false;
get_start_from_right 11;
get_start_from_right 10;
get_start_from_right 9;
get_start_from_right 8;
get_start_from_right 7;
get_start_from_right 6;
];
[
true;
false;
get_start_from_right 5;
get_start_from_right 4;
get_start_from_right 3;
get_start_from_right 2;
get_start_from_right 1;
get_start_from_right 0;
];
]
else if c <= 0x10FFFF then
[
[
true;
true;
true;
true;
false;
get_start_from_right 20;
get_start_from_right 19;
get_start_from_right 18;
];
[
true;
false;
get_start_from_right 17;
get_start_from_right 16;
get_start_from_right 15;
get_start_from_right 14;
get_start_from_right 13;
get_start_from_right 12;
];
[
true;
false;
get_start_from_right 11;
get_start_from_right 10;
get_start_from_right 9;
get_start_from_right 8;
get_start_from_right 7;
get_start_from_right 6;
];
[
true;
false;
get_start_from_right 5;
get_start_from_right 4;
get_start_from_right 3;
get_start_from_right 2;
get_start_from_right 1;
get_start_from_right 0;
];
]
else
failwith "Unexpected case"
in
chars |> List.map char_of_bit_list |> List.to_seq |> String.of_seq
let () =
Crowbar.add_test
~name:"ccutf8_string_uchar_to_bytes_is_same_as_simple_version"
[ Crowbar.range (succ 0x10FFFF) ]
(fun c ->
Crowbar.guard (Uchar.is_valid c);
let c = Uchar.of_int c in
let simple_answer = simple_uchar_to_string c in
let answer =
let buf = ref [] in
CCUtf8_string.uchar_to_bytes c (fun c -> buf := c :: !buf);
!buf |> List.rev |> List.to_seq |> String.of_seq
in
Crowbar.check_eq simple_answer answer)

6
fuzz/clean.sh Executable file
View file

@ -0,0 +1,6 @@
#!/bin/bash
script_dir=$(dirname $(readlink -f "$0"))
rm -r "$script_dir"/../fuzz-*-input
rm -r "$script_dir"/../fuzz-*-output

9
fuzz/dune Normal file
View file

@ -0,0 +1,9 @@
(executables
(flags
(-w "+a-4-9-29-37-40-42-44-48-50-32-70" -g))
(names
ccsexp_parse_string_does_not_crash
ccutf8_string_uchar_to_bytes_is_same_as_simple_version
ccsexp_csexp_reparse)
(optional)
(libraries crowbar containers))

15
fuzz/list.sh Executable file
View file

@ -0,0 +1,15 @@
#!/bin/bash
script_dir=$(dirname $(readlink -f "$0"))
echo "Building"
dune build @all
echo ""
echo "Fuzzing tests available:"
for file in "$script_dir"/../_build/default/fuzz/*.exe; do
echo "- "$(basename $file | sed 's/\.exe$//')
done

37
fuzz/run.sh Executable file
View file

@ -0,0 +1,37 @@
#!/bin/bash
script_dir=$(dirname $(readlink -f "$0"))
skip_build=$2
if [[ "$skip_build" != "skip_build" ]]; then
echo "Building"
dune build @all
fi
if [[ "$1" == "" ]]; then
echo "Please enter a fuzzing test to run"
exit 1
fi
name=$(echo "$1" | sed 's/\.exe$//' | sed 's/\.ml$//')
echo "Creating input directory"
input_dir="$script_dir"/../"fuzz-""$name""-input"
output_dir="$script_dir"/../"fuzz-""$name""-output"
mkdir -p "$input_dir"
echo "abcd" > "$input_dir"/dummy
if [ -d "$output_dir" ]; then
afl-fuzz -t 1000 -i - -o "$output_dir" "$script_dir"/../_build/default/fuzz/"$name".exe @@
else
mkdir -p "$output_dir"
afl-fuzz -t 1000 -i "$input_dir" -o "$output_dir" "$script_dir"/../_build/default/fuzz/"$name".exe @@
fi

126
fuzz/run_all.sh Executable file
View file

@ -0,0 +1,126 @@
#!/bin/bash
cpu_count=$(grep -c ^processor /proc/cpuinfo)
simul_test_count=$[cpu_count-1]
test_timeout="10m"
script_dir=$(dirname $(readlink -f "$0"))
log_dir="$script_dir"/../fuzz-logs
echo "Building"
dune build @all
echo ""
start_date=$(date "+%Y-%m-%d %H:%M")
start_time=$(date "+%s")
names=()
i=0
for file in "$script_dir"/../_build/default/fuzz/*.exe; do
name=$(basename $file | sed 's/\.exe$//')
names[$i]=$name
i=$[i+1]
done
test_count=${#names[@]}
echo "Fuzzing tests available:"
for name in ${names[@]}; do
echo "- "$name
done
echo ""
echo "Fuzzing start time:" $start_date
echo ""
echo "Starting $test_count tests"
echo ""
mkdir -p "$log_dir"
i=0
while (( $i < $test_count )); do
if (( $test_count - $i >= $simul_test_count )); then
tests_to_run=$simul_test_count
else
tests_to_run=$[test_count - i]
fi
echo "Running $tests_to_run tests in parallel"
for (( c=0; c < $tests_to_run; c++ )); do
name=${names[$i]}
if [[ "$name" != "" ]]; then
echo " Starting $name"
(AFL_NO_UI=1 timeout "$test_timeout" "$script_dir"/run.sh "$name" skip_build > "$log_dir"/"$name".log) &
i=$[i+1]
fi
done
echo "Waiting for $test_timeout"
sleep $test_timeout
echo "Terminating tests"
pkill afl-fuzz
sleep 5
echo ""
echo "$[test_count - i] / $test_count tests remaining"
echo ""
done
end_date=$(date "+%Y-%m-%d %H:%M")
end_time=$(date "+%s")
echo ""
echo "Test end:" $end_date
echo ""
echo "Time elapsed:" $[(end_time - start_time) / 60] "minutes"
test_fail_count=0
tests_failed=()
for name in ${names[@]}; do
output_dir="$script_dir"/../"fuzz-""$name""-output"
crashes_dir="$output_dir"/crashes
if [ -z "$(ls -A $crashes_dir)" ]; then
# crashes dir is empty
:
else
# crashes dir is not empty
test_fail_count=$[$test_fail_count + 1]
tests_failed+=("$name")
fi
done
echo "========================================"
if [[ $test_fail_count == 0 ]]; then
echo "All $test_count tests passed"
exit_code=0
else
echo "$test_fail_count tests failed"
echo ""
echo "List of tests failed :"
for t in ${tests_failed[@]}; do
echo " "$t
done
exit_code=1
fi

View file

@ -1,692 +0,0 @@
(* OASIS_START *)
(* DO NOT EDIT (digest: b119194f5742ac2f3cdceac9a223dda7) *)
module OASISGettext = struct
(* # 22 "src/oasis/OASISGettext.ml" *)
let ns_ str =
str
let s_ str =
str
let f_ (str: ('a, 'b, 'c, 'd) format4) =
str
let fn_ fmt1 fmt2 n =
if n = 1 then
fmt1^^""
else
fmt2^^""
let init =
[]
end
module OASISExpr = struct
(* # 22 "src/oasis/OASISExpr.ml" *)
open OASISGettext
type test = string
type flag = string
type t =
| EBool of bool
| ENot of t
| EAnd of t * t
| EOr of t * t
| EFlag of flag
| ETest of test * string
type 'a choices = (t * 'a) list
let eval var_get t =
let rec eval' =
function
| EBool b ->
b
| ENot e ->
not (eval' e)
| EAnd (e1, e2) ->
(eval' e1) && (eval' e2)
| EOr (e1, e2) ->
(eval' e1) || (eval' e2)
| EFlag nm ->
let v =
var_get nm
in
assert(v = "true" || v = "false");
(v = "true")
| ETest (nm, vl) ->
let v =
var_get nm
in
(v = vl)
in
eval' t
let choose ?printer ?name var_get lst =
let rec choose_aux =
function
| (cond, vl) :: tl ->
if eval var_get cond then
vl
else
choose_aux tl
| [] ->
let str_lst =
if lst = [] then
s_ "<empty>"
else
String.concat
(s_ ", ")
(List.map
(fun (cond, vl) ->
match printer with
| Some p -> p vl
| None -> s_ "<no printer>")
lst)
in
match name with
| Some nm ->
failwith
(Printf.sprintf
(f_ "No result for the choice list '%s': %s")
nm str_lst)
| None ->
failwith
(Printf.sprintf
(f_ "No result for a choice list: %s")
str_lst)
in
choose_aux (List.rev lst)
end
# 132 "myocamlbuild.ml"
module BaseEnvLight = struct
(* # 22 "src/base/BaseEnvLight.ml" *)
module MapString = Map.Make(String)
type t = string MapString.t
let default_filename =
Filename.concat
(Sys.getcwd ())
"setup.data"
let load ?(allow_empty=false) ?(filename=default_filename) () =
if Sys.file_exists filename then
begin
let chn =
open_in_bin filename
in
let st =
Stream.of_channel chn
in
let line =
ref 1
in
let st_line =
Stream.from
(fun _ ->
try
match Stream.next st with
| '\n' -> incr line; Some '\n'
| c -> Some c
with Stream.Failure -> None)
in
let lexer =
Genlex.make_lexer ["="] st_line
in
let rec read_file mp =
match Stream.npeek 3 lexer with
| [Genlex.Ident nm; Genlex.Kwd "="; Genlex.String value] ->
Stream.junk lexer;
Stream.junk lexer;
Stream.junk lexer;
read_file (MapString.add nm value mp)
| [] ->
mp
| _ ->
failwith
(Printf.sprintf
"Malformed data file '%s' line %d"
filename !line)
in
let mp =
read_file MapString.empty
in
close_in chn;
mp
end
else if allow_empty then
begin
MapString.empty
end
else
begin
failwith
(Printf.sprintf
"Unable to load environment, the file '%s' doesn't exist."
filename)
end
let rec var_expand str env =
let buff =
Buffer.create ((String.length str) * 2)
in
Buffer.add_substitute
buff
(fun var ->
try
var_expand (MapString.find var env) env
with Not_found ->
failwith
(Printf.sprintf
"No variable %s defined when trying to expand %S."
var
str))
str;
Buffer.contents buff
let var_get name env =
var_expand (MapString.find name env) env
let var_choose lst env =
OASISExpr.choose
(fun nm -> var_get nm env)
lst
end
# 237 "myocamlbuild.ml"
module MyOCamlbuildFindlib = struct
(* # 22 "src/plugins/ocamlbuild/MyOCamlbuildFindlib.ml" *)
(** OCamlbuild extension, copied from
* http://brion.inria.fr/gallium/index.php/Using_ocamlfind_with_ocamlbuild
* by N. Pouillard and others
*
* Updated on 2009/02/28
*
* Modified by Sylvain Le Gall
*)
open Ocamlbuild_plugin
type conf =
{ no_automatic_syntax: bool;
}
(* these functions are not really officially exported *)
let run_and_read =
Ocamlbuild_pack.My_unix.run_and_read
let blank_sep_strings =
Ocamlbuild_pack.Lexers.blank_sep_strings
let exec_from_conf exec =
let exec =
let env_filename = Pathname.basename BaseEnvLight.default_filename in
let env = BaseEnvLight.load ~filename:env_filename ~allow_empty:true () in
try
BaseEnvLight.var_get exec env
with Not_found ->
Printf.eprintf "W: Cannot get variable %s\n" exec;
exec
in
let fix_win32 str =
if Sys.os_type = "Win32" then begin
let buff = Buffer.create (String.length str) in
(* Adapt for windowsi, ocamlbuild + win32 has a hard time to handle '\\'.
*)
String.iter
(fun c -> Buffer.add_char buff (if c = '\\' then '/' else c))
str;
Buffer.contents buff
end else begin
str
end
in
fix_win32 exec
let split s ch =
let buf = Buffer.create 13 in
let x = ref [] in
let flush () =
x := (Buffer.contents buf) :: !x;
Buffer.clear buf
in
String.iter
(fun c ->
if c = ch then
flush ()
else
Buffer.add_char buf c)
s;
flush ();
List.rev !x
let split_nl s = split s '\n'
let before_space s =
try
String.before s (String.index s ' ')
with Not_found -> s
(* ocamlfind command *)
let ocamlfind x = S[Sh (exec_from_conf "ocamlfind"); x]
(* This lists all supported packages. *)
let find_packages () =
List.map before_space (split_nl & run_and_read (exec_from_conf "ocamlfind" ^ " list"))
(* Mock to list available syntaxes. *)
let find_syntaxes () = ["camlp4o"; "camlp4r"]
let well_known_syntax = [
"camlp4.quotations.o";
"camlp4.quotations.r";
"camlp4.exceptiontracer";
"camlp4.extend";
"camlp4.foldgenerator";
"camlp4.listcomprehension";
"camlp4.locationstripper";
"camlp4.macro";
"camlp4.mapgenerator";
"camlp4.metagenerator";
"camlp4.profiler";
"camlp4.tracer"
]
let dispatch conf =
function
| After_options ->
(* By using Before_options one let command line options have an higher
* priority on the contrary using After_options will guarantee to have
* the higher priority override default commands by ocamlfind ones *)
Options.ocamlc := ocamlfind & A"ocamlc";
Options.ocamlopt := ocamlfind & A"ocamlopt";
Options.ocamldep := ocamlfind & A"ocamldep";
Options.ocamldoc := ocamlfind & A"ocamldoc";
Options.ocamlmktop := ocamlfind & A"ocamlmktop";
Options.ocamlmklib := ocamlfind & A"ocamlmklib"
| After_rules ->
(* When one link an OCaml library/binary/package, one should use
* -linkpkg *)
flag ["ocaml"; "link"; "program"] & A"-linkpkg";
if not (conf.no_automatic_syntax) then begin
(* For each ocamlfind package one inject the -package option when
* compiling, computing dependencies, generating documentation and
* linking. *)
List.iter
begin fun pkg ->
let base_args = [A"-package"; A pkg] in
(* TODO: consider how to really choose camlp4o or camlp4r. *)
let syn_args = [A"-syntax"; A "camlp4o"] in
let (args, pargs) =
(* Heuristic to identify syntax extensions: whether they end in
".syntax"; some might not.
*)
if Filename.check_suffix pkg "syntax" ||
List.mem pkg well_known_syntax then
(syn_args @ base_args, syn_args)
else
(base_args, [])
in
flag ["ocaml"; "compile"; "pkg_"^pkg] & S args;
flag ["ocaml"; "ocamldep"; "pkg_"^pkg] & S args;
flag ["ocaml"; "doc"; "pkg_"^pkg] & S args;
flag ["ocaml"; "link"; "pkg_"^pkg] & S base_args;
flag ["ocaml"; "infer_interface"; "pkg_"^pkg] & S args;
(* TODO: Check if this is allowed for OCaml < 3.12.1 *)
flag ["ocaml"; "compile"; "package("^pkg^")"] & S pargs;
flag ["ocaml"; "ocamldep"; "package("^pkg^")"] & S pargs;
flag ["ocaml"; "doc"; "package("^pkg^")"] & S pargs;
flag ["ocaml"; "infer_interface"; "package("^pkg^")"] & S pargs;
end
(find_packages ());
end;
(* Like -package but for extensions syntax. Morover -syntax is useless
* when linking. *)
List.iter begin fun syntax ->
flag ["ocaml"; "compile"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
flag ["ocaml"; "ocamldep"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
flag ["ocaml"; "doc"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
flag ["ocaml"; "infer_interface"; "syntax_"^syntax] &
S[A"-syntax"; A syntax];
end (find_syntaxes ());
(* The default "thread" tag is not compatible with ocamlfind.
* Indeed, the default rules add the "threads.cma" or "threads.cmxa"
* options when using this tag. When using the "-linkpkg" option with
* ocamlfind, this module will then be added twice on the command line.
*
* To solve this, one approach is to add the "-thread" option when using
* the "threads" package using the previous plugin.
*)
flag ["ocaml"; "pkg_threads"; "compile"] (S[A "-thread"]);
flag ["ocaml"; "pkg_threads"; "doc"] (S[A "-I"; A "+threads"]);
flag ["ocaml"; "pkg_threads"; "link"] (S[A "-thread"]);
flag ["ocaml"; "pkg_threads"; "infer_interface"] (S[A "-thread"]);
flag ["ocaml"; "package(threads)"; "compile"] (S[A "-thread"]);
flag ["ocaml"; "package(threads)"; "doc"] (S[A "-I"; A "+threads"]);
flag ["ocaml"; "package(threads)"; "link"] (S[A "-thread"]);
flag ["ocaml"; "package(threads)"; "infer_interface"] (S[A "-thread"]);
| _ ->
()
end
module MyOCamlbuildBase = struct
(* # 22 "src/plugins/ocamlbuild/MyOCamlbuildBase.ml" *)
(** Base functions for writing myocamlbuild.ml
@author Sylvain Le Gall
*)
open Ocamlbuild_plugin
module OC = Ocamlbuild_pack.Ocaml_compiler
type dir = string
type file = string
type name = string
type tag = string
(* # 62 "src/plugins/ocamlbuild/MyOCamlbuildBase.ml" *)
type t =
{
lib_ocaml: (name * dir list * string list) list;
lib_c: (name * dir * file list) list;
flags: (tag list * (spec OASISExpr.choices)) list;
(* Replace the 'dir: include' from _tags by a precise interdepends in
* directory.
*)
includes: (dir * dir list) list;
}
let env_filename =
Pathname.basename
BaseEnvLight.default_filename
let dispatch_combine lst =
fun e ->
List.iter
(fun dispatch -> dispatch e)
lst
let tag_libstubs nm =
"use_lib"^nm^"_stubs"
let nm_libstubs nm =
nm^"_stubs"
let dispatch t e =
let env =
BaseEnvLight.load
~filename:env_filename
~allow_empty:true
()
in
match e with
| Before_options ->
let no_trailing_dot s =
if String.length s >= 1 && s.[0] = '.' then
String.sub s 1 ((String.length s) - 1)
else
s
in
List.iter
(fun (opt, var) ->
try
opt := no_trailing_dot (BaseEnvLight.var_get var env)
with Not_found ->
Printf.eprintf "W: Cannot get variable %s\n" var)
[
Options.ext_obj, "ext_obj";
Options.ext_lib, "ext_lib";
Options.ext_dll, "ext_dll";
]
| After_rules ->
(* Declare OCaml libraries *)
List.iter
(function
| nm, [], intf_modules ->
ocaml_lib nm;
let cmis =
List.map (fun m -> (String.uncapitalize m) ^ ".cmi")
intf_modules in
dep ["ocaml"; "link"; "library"; "file:"^nm^".cma"] cmis
| nm, dir :: tl, intf_modules ->
ocaml_lib ~dir:dir (dir^"/"^nm);
List.iter
(fun dir ->
List.iter
(fun str ->
flag ["ocaml"; "use_"^nm; str] (S[A"-I"; P dir]))
["compile"; "infer_interface"; "doc"])
tl;
let cmis =
List.map (fun m -> dir^"/"^(String.uncapitalize m)^".cmi")
intf_modules in
dep ["ocaml"; "link"; "library"; "file:"^dir^"/"^nm^".cma"]
cmis)
t.lib_ocaml;
(* Declare directories dependencies, replace "include" in _tags. *)
List.iter
(fun (dir, include_dirs) ->
Pathname.define_context dir include_dirs)
t.includes;
(* Declare C libraries *)
List.iter
(fun (lib, dir, headers) ->
(* Handle C part of library *)
flag ["link"; "library"; "ocaml"; "byte"; tag_libstubs lib]
(S[A"-dllib"; A("-l"^(nm_libstubs lib)); A"-cclib";
A("-l"^(nm_libstubs lib))]);
flag ["link"; "library"; "ocaml"; "native"; tag_libstubs lib]
(S[A"-cclib"; A("-l"^(nm_libstubs lib))]);
flag ["link"; "program"; "ocaml"; "byte"; tag_libstubs lib]
(S[A"-dllib"; A("dll"^(nm_libstubs lib))]);
(* When ocaml link something that use the C library, then one
need that file to be up to date.
This holds both for programs and for libraries.
*)
dep ["link"; "ocaml"; tag_libstubs lib]
[dir/"lib"^(nm_libstubs lib)^"."^(!Options.ext_lib)];
dep ["compile"; "ocaml"; tag_libstubs lib]
[dir/"lib"^(nm_libstubs lib)^"."^(!Options.ext_lib)];
(* TODO: be more specific about what depends on headers *)
(* Depends on .h files *)
dep ["compile"; "c"]
headers;
(* Setup search path for lib *)
flag ["link"; "ocaml"; "use_"^lib]
(S[A"-I"; P(dir)]);
)
t.lib_c;
(* Add flags *)
List.iter
(fun (tags, cond_specs) ->
let spec = BaseEnvLight.var_choose cond_specs env in
let rec eval_specs =
function
| S lst -> S (List.map eval_specs lst)
| A str -> A (BaseEnvLight.var_expand str env)
| spec -> spec
in
flag tags & (eval_specs spec))
t.flags
| _ ->
()
let dispatch_default conf t =
dispatch_combine
[
dispatch t;
MyOCamlbuildFindlib.dispatch conf;
]
end
# 606 "myocamlbuild.ml"
open Ocamlbuild_plugin;;
let package_default =
{
MyOCamlbuildBase.lib_ocaml =
[
("containers", ["src/core"], []);
("containers_io", ["src/io"], []);
("containers_unix", ["src/unix"], []);
("containers_sexp", ["src/sexp"], []);
("containers_data", ["src/data"], []);
("containers_iter", ["src/iter"], []);
("containers_string", ["src/string"], []);
("containers_advanced", ["src/advanced"], []);
("containers_bigarray", ["src/bigarray"], []);
("containers_thread", ["src/threads"], []);
("containers_top", ["src/top"], [])
];
lib_c = [];
flags = [];
includes =
[
("src/top",
[
"src/bigarray";
"src/core";
"src/data";
"src/iter";
"src/sexp";
"src/string";
"src/unix"
]);
("src/threads", ["src/core"]);
("src/bigarray", ["src/core"]);
("src/advanced", ["src/core"]);
("qtest",
[
"src/advanced";
"src/bigarray";
"src/core";
"src/data";
"src/io";
"src/iter";
"src/sexp";
"src/string";
"src/threads";
"src/unix"
]);
("examples", ["src/sexp"]);
("benchs",
[
"src/advanced";
"src/core";
"src/data";
"src/iter";
"src/string";
"src/threads"
])
]
}
;;
let conf = {MyOCamlbuildFindlib.no_automatic_syntax = false}
let dispatch_default = MyOCamlbuildBase.dispatch_default conf package_default;;
# 673 "myocamlbuild.ml"
(* OASIS_STOP *)
let doc_intro = "doc/intro.txt"
open Ocamlbuild_plugin;;
dispatch
(MyOCamlbuildBase.dispatch_combine [
begin function
| After_rules ->
(* Documentation index *)
dep ["ocaml"; "doc"; "extension:html"] & [doc_intro] ;
flag ["ocaml"; "doc"; "extension:html"]
& S[A"-t"; A"Containers doc"; A"-intro"; P doc_intro ];
| _ -> ()
end;
dispatch_default
])

44
opam
View file

@ -1,44 +0,0 @@
opam-version: "1.2"
name: "containers"
version: "dev"
author: "Simon Cruanes"
maintainer: "simon.cruanes@inria.fr"
build: [
["./configure"
"--prefix" prefix
"--disable-bench"
"--disable-tests"
"--%{base-unix:enable}%-unix"
"--enable-docs"
]
[make "build"]
]
install: [
[make "install"]
]
build-doc: [ make "doc" ]
build-test: [ make "test" ]
remove: [
["ocamlfind" "remove" "containers"]
]
depends: [
"ocamlfind" {build}
"oasis" {build}
"base-bytes"
"result"
"ocamlbuild" {build}
]
depopts: [
"base-unix"
"base-threads"
"qtest" { test }
]
conflicts: [
"sequence" { < "0.5" }
]
tags: [ "stdlib" "containers" "iterators" "list" "heap" "queue" ]
homepage: "https://github.com/c-cube/ocaml-containers/"
doc: "http://cedeela.fr/~simon/software/containers/"
available: [ocaml-version >= "4.01.0"]
dev-repo: "https://github.com/c-cube/ocaml-containers.git"
bug-reports: "https://github.com/c-cube/ocaml-containers/issues/"

4
run_bench_hash.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
OPTS="--profile=release --display=quiet"
exec dune exec $OPTS -- benchs/run_benchs_hash.exe $@

4
run_benchs.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
OPTS="--profile=release --display=quiet"
exec dune exec $OPTS -- benchs/run_benchs.exe $@

View file

@ -1,32 +0,0 @@
(* setup.ml generated for the first time by OASIS v0.4.4 *)
(* OASIS_START *)
(* DO NOT EDIT (digest: 1bc19e72587da58c1e1f99f847b509aa) *)
(******************************************************************************)
(* OASIS: architecture for building OCaml libraries and applications *)
(* *)
(* Copyright (C) 2011-2016, Sylvain Le Gall *)
(* Copyright (C) 2008-2011, OCamlCore SARL *)
(* *)
(* This library is free software; you can redistribute it and/or modify it *)
(* under the terms of the GNU Lesser General Public License as published by *)
(* the Free Software Foundation; either version 2.1 of the License, or (at *)
(* your option) any later version, with the OCaml static compilation *)
(* exception. *)
(* *)
(* This library is distributed in the hope that it will be useful, but *)
(* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *)
(* or FITNESS FOR A PARTICULAR PURPOSE. See the file COPYING for more *)
(* details. *)
(* *)
(* You should have received a copy of the GNU Lesser General Public License *)
(* along with this library; if not, write to the Free Software Foundation, *)
(* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *)
(******************************************************************************)
open OASISDynRun
let setup_t = BaseCompat.Compat_0_4.adapt_setup_t setup_t
open BaseCompat.Compat_0_4
(* OASIS_STOP *)
let () = setup ();;

View file

@ -0,0 +1,183 @@
module Str_map = Map.Make (String)
type t =
| Int of int64
| String of string
| List of t list
| Map of t Str_map.t
let rec equal t1 t2 =
match t1, t2 with
| Int i1, Int i2 -> i1 = i2
| String s1, String s2 -> s1 = s2
| List l1, List l2 ->
(try List.for_all2 equal l1 l2 with Invalid_argument _ -> false)
| Map d1, Map d2 -> Str_map.equal equal d1 d2
| (Int _ | String _ | List _ | Map _), _ -> false
let rec hash t =
let module H = CCHash in
match t with
| Int i -> H.int64 i
| String s -> H.combine2 10 (H.string s)
| List l -> H.combine2 20 (H.list hash l)
| Map l ->
H.combine2 30
( H.iter (H.pair H.string hash) @@ fun k ->
Str_map.iter (fun x y -> k (x, y)) l )
let int64 i : t = Int i
let int i : t = int64 (Int64.of_int i)
let string s : t = String s
let list l : t = List l
let map m : t = Map m
let map_of_list l : t =
map @@ List.fold_left (fun m (k, v) -> Str_map.add k v m) Str_map.empty l
let rec pp_debug out (self : t) : unit =
let fpf = Format.fprintf in
match self with
| Int i -> fpf out "%Ld" i
| String s -> fpf out "%S" s
| List l ->
fpf out "[@[<hv>";
List.iteri
(fun i v ->
if i > 0 then fpf out ";@ ";
pp_debug out v)
l;
fpf out "@]]"
| Map m ->
fpf out "{@[<hv>";
let i = ref 0 in
Str_map.iter
(fun k v ->
if !i > 0 then fpf out ";@ ";
incr i;
fpf out "@[<1>%S:@ %a@]" k pp_debug v)
m;
fpf out "@]}"
let to_string_debug self = Format.asprintf "%a" pp_debug self
module Encode = struct
let bpf = Printf.bprintf
let fpf = Printf.fprintf
let rec to_buffer (buf : Buffer.t) (self : t) : unit =
let recurse = to_buffer buf in
let addc = Buffer.add_char in
match self with
| Int i -> bpf buf "i%Lde" i
| String s -> bpf buf "%d:%s" (String.length s) s
| List l ->
addc buf 'l';
List.iter recurse l;
addc buf 'e'
| Map l ->
addc buf 'd';
Str_map.iter
(fun k v -> bpf buf "%d:%s%a" (String.length k) k to_buffer v)
l;
addc buf 'e'
let to_string (self : t) : string =
let buf = Buffer.create 32 in
to_buffer buf self;
Buffer.contents buf
let rec to_chan (oc : out_channel) (self : t) : unit =
let recurse = to_chan oc in
let addc = output_char in
match self with
| Int i -> fpf oc "i%Lde" i
| String s -> fpf oc "%d:%s" (String.length s) s
| List l ->
addc oc 'l';
List.iter recurse l;
addc oc 'e'
| Map l ->
addc oc 'd';
Str_map.iter (fun k v -> fpf oc "%d:%s%a" (String.length k) k to_chan v) l;
addc oc 'e'
let to_fmt out self = Format.pp_print_string out (to_string self)
end
module Decode = struct
exception Fail
let of_string s =
let i = ref 0 in
let[@inline] check_not_eof () =
if !i >= String.length s then raise_notrace Fail
in
let rec top () : t =
check_not_eof ();
match String.unsafe_get s !i with
| 'l' ->
incr i;
read_list []
| 'd' ->
incr i;
read_map Str_map.empty
| 'i' ->
incr i;
let n = read_int 'e' true 0 in
int n
| '0' .. '9' -> String (parse_str_len ())
| _ -> raise_notrace Fail
(* read integer until char [stop] is met, consume [stop], return int *)
and read_int stop sign n : int =
check_not_eof ();
match String.unsafe_get s !i with
| c when c == stop ->
incr i;
if sign then
n
else
-n
| '-' when stop == 'e' && sign && n = 0 ->
incr i;
read_int stop false n
| '0' .. '9' as c ->
incr i;
read_int stop sign (Char.code c - Char.code '0' + (10 * n))
| _ -> raise_notrace Fail
and parse_str_len () : string =
let n = read_int ':' true 0 in
if !i + n > String.length s then raise_notrace Fail;
let s = String.sub s !i n in
i := !i + n;
s
and read_list acc =
check_not_eof ();
match String.unsafe_get s !i with
| 'e' ->
incr i;
List (List.rev acc)
| _ ->
let x = top () in
read_list (x :: acc)
and read_map acc =
check_not_eof ();
match String.unsafe_get s !i with
| 'e' ->
incr i;
Map acc
| _ ->
let k = parse_str_len () in
let v = top () in
read_map (Str_map.add k v acc)
in
try Some (top ()) with Fail -> None
let of_string_exn s =
match of_string s with
| Some x -> x
| None -> failwith "bencode.decode: invalid string"
end

View file

@ -0,0 +1,44 @@
(** Basic Bencode decoder/encoder.
See https://en.wikipedia.org/wiki/Bencode .
@since 3.8 *)
module Str_map : module type of Map.Make (String)
type t =
| Int of int64
| String of string
| List of t list
| Map of t Str_map.t
val equal : t -> t -> bool
val hash : t -> int
val pp_debug : Format.formatter -> t -> unit
(** Printer for diagnostic/human consumption *)
val to_string_debug : t -> string
val int : int -> t
val int64 : int64 -> t
val string : string -> t
val list : t list -> t
val map_of_list : (string * t) list -> t
val map : t Str_map.t -> t
(** Encoding *)
module Encode : sig
val to_string : t -> string
val to_buffer : Buffer.t -> t -> unit
val to_chan : out_channel -> t -> unit
val to_fmt : Format.formatter -> t -> unit
end
(** Decoding *)
module Decode : sig
val of_string : string -> t option
val of_string_exn : string -> t
(** Parse string.
@raise Failure if the string is not valid bencode. *)
end

5
src/bencode/dune Normal file
View file

@ -0,0 +1,5 @@
(library
(name containers_bencode)
(public_name containers.bencode)
(libraries containers)
(synopsis "Bencode codec for containers (the format for bittorrent files)"))

323
src/cbor/containers_cbor.ml Normal file
View file

@ -0,0 +1,323 @@
module Fmt = CCFormat
type t =
[ `Null
| `Undefined
| `Simple of int
| `Bool of bool
| `Int of int64
| `Float of float
| `Bytes of string
| `Text of string
| `Array of t list
| `Map of (t * t) list
| `Tag of int * t
]
let rec pp_diagnostic out (self : t) =
match self with
| `Null -> Fmt.string out "null"
| `Undefined -> Fmt.string out "undefined"
| `Simple i -> Fmt.fprintf out "simple(%d)" i
| `Bool b -> Fmt.bool out b
| `Int i -> Fmt.int64 out i
| `Float f -> Fmt.float out f
| `Bytes b -> Fmt.fprintf out "h'%s'" (CCString.to_hex b)
| `Text s -> Fmt.fprintf out "%S" s
| `Array l ->
Fmt.fprintf out "[@[";
List.iteri
(fun i x ->
if i > 0 then Fmt.fprintf out ",@ ";
pp_diagnostic out x)
l;
Fmt.fprintf out "@]]"
| `Map l ->
Fmt.fprintf out "{@[";
List.iteri
(fun i (k, v) ->
if i > 0 then Fmt.fprintf out ",@ ";
Fmt.fprintf out "@[%a:@ %a@]" pp_diagnostic k pp_diagnostic v)
l;
Fmt.fprintf out "@]}"
| `Tag (i, x) -> Fmt.fprintf out "%d(@[%a@])" i pp_diagnostic x
let to_string_diagnostic (self : t) : string =
Format.asprintf "@[<h>%a@]" pp_diagnostic self
exception Indefinite
let[@inline] i64_to_int i =
let j = Int64.to_int i in
if Int64.(of_int j = i) then
j
else
failwith "int64 does not fit in int"
let decode_exn (s : string) : t =
let b = Bytes.unsafe_of_string s in
let i = ref 0 in
(* currently at end delimiter? *)
let[@inline] is_break_stop_code () = Char.code s.[!i] = 0b111_11111 in
let[@inline] read_i8 () =
let c = Char.code s.[!i] in
incr i;
c
in
let[@inline] read_i16 () =
let c = Bytes.get_uint16_be b !i in
i := !i + 2;
c
in
let[@inline] read_i32 () =
let c = Bytes.get_int32_be b !i in
i := !i + 4;
c
in
let[@inline] read_i64 () =
let c = Bytes.get_int64_be b !i in
i := !i + 8;
c
in
let reserve_n n =
let j = !i in
if j + n > String.length s then failwith "cbor: cannot extract slice";
i := !i + n;
j
in
(* read integer value from least significant bits *)
let read_int ~allow_indefinite low =
match low with
| _ when low < 0 -> failwith "cbor: invalid length"
| _ when low < 24 -> Int64.of_int low
| 24 -> Int64.of_int (read_i8 ())
| 25 -> Int64.of_int (read_i16 ())
| 26 -> Int64.of_int32 (read_i32 ())
| 27 -> read_i64 ()
| 28 | 29 | 30 -> failwith "cbor: invalid length"
| 31 ->
if allow_indefinite then
raise_notrace Indefinite
else
failwith "cbor: invalid integer 31 in this context"
| _ -> assert false
in
(* appendix D
double decode_half(unsigned char *halfp) {
unsigned half = (halfp[0] << 8) + halfp[1];
unsigned exp = (half >> 10) & 0x1f;
unsigned mant = half & 0x3ff;
double val;
if (exp == 0) val = ldexp(mant, -24);
else if (exp != 31) val = ldexp(mant + 1024, exp - 25);
else val = mant == 0 ? INFINITY : NAN;
return half & 0x8000 ? -val : val;
}
*)
let decode_f16 (half : int) : float =
(* exponent is bits 15:10 *)
let exp = (half lsr 10) land 0x1f in
(* mantissa is bits 9:0 *)
let mant = half land 0x3ff in
let value =
if exp = 0 then
ldexp (float mant) (-24)
else if exp <> 31 then
ldexp (float (mant + 1024)) (exp - 25)
else if mant = 0 then
infinity
else
nan
in
if half land 0x8000 <> 0 then
-.value
else
value
in
(* roughly follow https://www.rfc-editor.org/rfc/rfc8949.html#pseudocode *)
let rec read_value () =
let c = read_i8 () in
let high = (c land 0b111_00000) lsr 5 in
let low = c land 0b000_11111 in
match high with
| 0 -> `Int (read_int ~allow_indefinite:false low)
| 1 ->
let i = read_int ~allow_indefinite:false low in
`Int Int64.(sub minus_one i)
| 2 ->
let s = read_bytes ~ty:`Bytes low in
`Bytes s
| 3 ->
let s = read_bytes ~ty:`String low in
`Text s
| 4 ->
let l =
match read_int ~allow_indefinite:true low |> i64_to_int with
| len -> List.init len (fun _ -> read_value ())
| exception Indefinite ->
let l = ref [] in
while not (is_break_stop_code ()) do
l := read_value () :: !l
done;
incr i;
(* consume stop code *)
List.rev !l
in
`Array l
| 5 ->
let l =
match read_int ~allow_indefinite:true low |> i64_to_int with
| len -> List.init len (fun _ -> read_pair ())
| exception Indefinite ->
let l = ref [] in
while not (is_break_stop_code ()) do
l := read_pair () :: !l
done;
incr i;
(* consume stop code *)
List.rev !l
in
`Map l
| 6 ->
let tag = read_int ~allow_indefinite:false low |> i64_to_int in
let v = read_value () in
`Tag (tag, v)
| 7 ->
(* simple or float,
https://www.rfc-editor.org/rfc/rfc8949.html#fpnocont *)
let i = read_int ~allow_indefinite:false low in
(match low with
| 20 -> `Bool false
| 21 -> `Bool true
| 22 -> `Null
| 23 -> `Undefined
| _ when low <= 24 -> `Simple (i64_to_int i)
| 25 ->
(* float16 *)
`Float (decode_f16 (Int64.to_int i))
| 26 ->
(* float 32 *)
`Float (Int32.float_of_bits (Int64.to_int32 i))
| 27 ->
(* float 64 *)
`Float (Int64.float_of_bits i)
| 28 | 29 | 30 -> failwith "cbor: malformed"
| 31 -> failwith "uncaught 'break' stop code"
| _ -> assert false (* unreachable *))
| _ ->
(* unreachable *)
assert false
and read_bytes ~ty low =
match read_int ~allow_indefinite:true low |> i64_to_int with
| exception Indefinite ->
let buf = Buffer.create 32 in
while not (is_break_stop_code ()) do
match read_value (), ty with
| `Text s, `String | `Bytes s, `Bytes -> Buffer.add_string buf s
| _ -> failwith "cbor: invalid chunk in indefinite length string/byte"
done;
incr i;
(* consume stop code *)
Buffer.contents buf
| len ->
let off = reserve_n len in
String.sub s off len
and read_pair () =
let k = read_value () in
let v = read_value () in
k, v
in
read_value ()
let decode s = try Ok (decode_exn s) with Failure s -> Error s
let encode ?(buf = Buffer.create 32) (self : t) : string =
Buffer.clear buf;
let[@inline] add_byte (high : int) (low : int) =
let i = (high lsl 5) lor low in
assert (i land 0xff == i);
Buffer.add_char buf (Char.unsafe_chr i)
in
let add_i64 (i : int64) = Buffer.add_int64_be buf i in
(* add unsigned integer, including first tag byte *)
let add_uint (high : int) (x : int64) =
assert (x >= 0L);
if x < 24L then
add_byte high (i64_to_int x)
else if x <= 0xffL then (
add_byte high 24;
Buffer.add_char buf (Char.unsafe_chr (i64_to_int x))
) else if x <= 0xff_ffL then (
add_byte high 25;
Buffer.add_uint16_be buf (i64_to_int x)
) else if x <= 0xff_ff_ff_ffL then (
add_byte high 26;
Buffer.add_int32_be buf (Int64.to_int32 x)
) else (
add_byte high 27;
Buffer.add_int64_be buf x
)
in
let rec encode_val (self : t) : unit =
match self with
| `Bool false -> add_byte 7 20
| `Bool true -> add_byte 7 21
| `Null -> add_byte 7 22
| `Undefined -> add_byte 7 23
| `Simple i ->
if i < 24 then
add_byte 7 i
else if i <= 0xff then (
add_byte 7 24;
Buffer.add_char buf (Char.unsafe_chr i)
) else
failwith "cbor: simple value too high (above 255)"
| `Float f ->
add_byte 7 27;
(* float 64 *)
add_i64 (Int64.bits_of_float f)
| `Array l ->
add_uint 4 (Int64.of_int (List.length l));
List.iter encode_val l
| `Map l ->
add_uint 5 (Int64.of_int (List.length l));
List.iter
(fun (k, v) ->
encode_val k;
encode_val v)
l
| `Text s ->
add_uint 3 (Int64.of_int (String.length s));
Buffer.add_string buf s
| `Bytes s ->
add_uint 2 (Int64.of_int (String.length s));
Buffer.add_string buf s
| `Tag (t, v) ->
add_uint 6 (Int64.of_int t);
encode_val v
| `Int i ->
if i >= Int64.zero then
add_uint 0 i
else if Int64.(add min_int 2L) > i then (
(* large negative int, be careful. encode [(-i)-1] via int64. *)
add_byte 1 27;
Buffer.add_int64_be buf Int64.(neg (add 1L i))
) else
add_uint 1 Int64.(sub (neg i) one)
in
encode_val self;
Buffer.contents buf

View file

@ -0,0 +1,32 @@
(** CBOR encoder/decoder.
The type is chosen to be compatible with ocaml-cbor.
See {{: https://www.rfc-editor.org/rfc/rfc8949.html} the RFC}.
{b note} this is experimental.
@since 3.9
*)
type t =
[ `Null
| `Undefined
| `Simple of int
| `Bool of bool
| `Int of int64
| `Float of float
| `Bytes of string
| `Text of string
| `Array of t list
| `Map of (t * t) list
| `Tag of int * t
]
val pp_diagnostic : t CCFormat.printer
val to_string_diagnostic : t -> string
val encode : ?buf:Buffer.t -> t -> string
val decode : string -> (t, string) result
val decode_exn : string -> t
(** Like {!decode}.
@raise Failure if the string isn't valid *)

7
src/cbor/dune Normal file
View file

@ -0,0 +1,7 @@
(library
(name containers_cbor)
(libraries containers)
(preprocess
(action
(run %{project_root}/src/core/cpp/cpp.exe %{input-file})))
(public_name containers.cbor))

3671
src/cbor/rfc8949.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,143 @@
(** {1 Code generators} *)
module Fmt = CCFormat
let spf = Printf.sprintf
let fpf = Fmt.fprintf
type code =
| Base of { pp: unit Fmt.printer }
| Struct of string * code list
| Sig of string * code list
module Code = struct
type t = code
let in_struct m (cs : t list) : t = Struct (m, cs)
let in_sig m (cs : t list) : t = Sig (m, cs)
let rec pp_rec out c =
let ppl = Fmt.(list ~sep:(return "@ ") pp_rec) in
match c with
| Base { pp } -> pp out ()
| Struct (m, cs) ->
fpf out "@[<hv2>module %s = struct@ %a@;<1 -2>end@]" m ppl cs
| Sig (m, cs) -> fpf out "@[<hv2>module %s : sig@ %a@;<1 -2>end@]" m ppl cs
let pp out c = fpf out "@[<v>%a@]" pp_rec c
let to_string c = Fmt.to_string pp c
let mk_pp pp = Base { pp }
let mk_str s = Base { pp = Fmt.const Fmt.string s }
end
module Bitfield = struct
type field = {
f_name: string;
f_offset: int;
f_def: field_def;
}
and field_def =
| F_bit
| F_int of { width: int }
type t = {
name: string;
mutable fields: field list;
mutable width: int;
emit_failure_if_too_wide: bool;
}
let make ?(emit_failure_if_too_wide = true) ~name () : t =
{ name; fields = []; width = 0; emit_failure_if_too_wide }
let total_width self = self.width
let field_bit self f_name =
let f_offset = total_width self in
let f = { f_name; f_offset; f_def = F_bit } in
self.fields <- f :: self.fields;
self.width <- 1 + self.width
let field_int self ~width f_name : unit =
let f_offset = total_width self in
let f = { f_name; f_offset; f_def = F_int { width } } in
self.fields <- f :: self.fields;
self.width <- self.width + width
let empty_name self =
if self.name = "t" then
"empty"
else
spf "empty_%s" self.name
let gen_ml self : code =
Code.mk_pp @@ fun out () ->
fpf out "@[<v>type %s = int@," self.name;
fpf out "@[let %s : %s = 0@]@," (empty_name self) self.name;
List.iter
(fun f ->
let inline = "[@inline]" in
(* TODO: option to enable/disable that *)
let off = f.f_offset in
match f.f_def with
| F_bit ->
let x_lsr =
if off = 0 then
"x"
else
spf "(x lsr %d)" off
in
fpf out "@[let%s get_%s (x:%s) : bool = (%s land 1) <> 0@]@," inline
f.f_name self.name x_lsr;
let mask_shifted = 1 lsl off in
fpf out
"@[<2>let%s set_%s (v:bool) (x:%s) : %s =@ if v then x lor %d else \
x land (lnot %d)@]@,"
inline f.f_name self.name self.name mask_shifted mask_shifted
| F_int { width } ->
let mask0 = (1 lsl width) - 1 in
fpf out "@[let%s get_%s (x:%s) : int = ((x lsr %d) land %d)@]@,"
inline f.f_name self.name off mask0;
fpf out
"@[<2>let%s set_%s (i:int) (x:%s) : %s =@ assert ((i land %d) == \
i);@ ((x land (lnot %d)) lor (i lsl %d))@]@,"
inline f.f_name self.name self.name mask0 (mask0 lsl off) off)
(List.rev self.fields);
(* check width *)
if self.emit_failure_if_too_wide then
fpf out
"(* check that int size is big enough *)@,\
@[let () = assert (Sys.int_size >= %d);;@]"
(total_width self);
fpf out "@]"
let gen_mli self : code =
Code.mk_pp @@ fun out () ->
fpf out "@[<v>type %s = private int@," self.name;
fpf out "@[<v>val %s : %s@," (empty_name self) self.name;
List.iter
(fun f ->
match f.f_def with
| F_bit ->
fpf out "@[val get_%s : %s -> bool@]@," f.f_name self.name;
fpf out "@[val set_%s : bool -> %s -> %s@]@," f.f_name self.name
self.name
| F_int { width } ->
fpf out "@[val get_%s : %s -> int@]@," f.f_name self.name;
fpf out
"@,@[(** %d bits integer *)@]@,@[val set_%s : int -> %s -> %s@]@,"
width f.f_name self.name self.name)
(List.rev self.fields);
fpf out "@]"
end
let emit_chan oc cs =
let fmt = Fmt.formatter_of_out_channel oc in
List.iter (fun c -> Fmt.fprintf fmt "@[%a@]@." Code.pp c) cs;
Fmt.fprintf fmt "@?"
let emit_file file cs = CCIO.with_out file (fun oc -> emit_chan oc cs)
let emit_string cs : string =
Fmt.asprintf "@[<v>%a@]" (Fmt.list ~sep:(Fmt.return "@ ") Code.pp) cs

View file

@ -0,0 +1,86 @@
(** {1 Code generators}
The code generator library is designed to be used from a build system
(for example, from [dune]) to generate efficient code for features
that are harder to provide at runtime.
The idea is that the build system should invoke some OCaml script
that depends on [containers.codegen]; the script uses the DSL below
to describe what code to generate (e.g. a description of a bitfield type)
and emits a [.ml] file (and possibly a [.mli] file).
For example, the build script might contain:
{[
module CG = Containers_codegen
let () =
let module B = CG.Bitfield in
let b = B.make ~name:"t" () in
B.field_bit b "x";
B.field_bit b "y";
B.field_bit b "z";
B.field_int b ~width:5 "foo";
CG.emit_file "foo.mli" [B.gen_mli b];
CG.emit_file "foo.ml" [B.gen_ml b];
()
]}
and this will produce [foo.ml] and [foo.mli] with a bitfield containing
[x], [y], and [z].
*)
module Fmt = CCFormat
type code
(** {2 Representation of OCaml code} *)
module Code : sig
type t = code
val pp : t Fmt.printer
val to_string : t -> string
val mk_pp : unit Fmt.printer -> t
val mk_str : string -> t
val in_struct : string -> t list -> t
val in_sig : string -> t list -> t
end
(** {2 Generate efficient bitfields that fit in an integer} *)
module Bitfield : sig
type t
val make : ?emit_failure_if_too_wide:bool -> name:string -> unit -> t
(** Make a new bitfield with the given name.
@param name the name of the generated type
@param emit_failure_if_too_wide if true, generated code includes a runtime
assertion that {!Sys.int_size} is wide enough to support this type *)
val field_bit : t -> string -> unit
(** [field_bit ty name] adds a field of size [1] to the bitfield [ty],
with name [name]. The generate code will provide get/set for
a boolean. *)
val field_int : t -> width:int -> string -> unit
(** [field_int ty name ~width] adds a field of size [width] to
the bitfield with name [name].
The accessors will be for integers of [width] bits, and the
setter might assert that the provided integer fits. *)
val total_width : t -> int
(** Total width in bits of the given bitfield. *)
val gen_mli : t -> code
(** Generate code for the type signature for the given bitfield *)
val gen_ml : t -> code
(** Generate code for the implementation for the given bitfield *)
end
val emit_file : string -> code list -> unit
(** [emit_file file cs] emits code fragments [cs] into the given file
at path [file] *)
val emit_chan : out_channel -> code list -> unit
val emit_string : code list -> string

6
src/codegen/dune Normal file
View file

@ -0,0 +1,6 @@
(library
(name containers_codegen)
(public_name containers.codegen)
(synopsis "code generators for Containers")
(libraries containers)
(flags :standard -warn-error -a+8))

25
src/codegen/tests/dune Normal file
View file

@ -0,0 +1,25 @@
; emit tests
(executable
(name emit_tests)
(modules emit_tests)
(flags :standard -warn-error -a+8)
(libraries containers containers.codegen))
(rule
(targets test_bitfield.ml test_bitfield.mli)
(action
(run ./emit_tests.exe)))
; run tests
(executables
(names test_bitfield)
(modules test_bitfield)
(flags :standard -warn-error -a+8)
(libraries containers))
(rule
(alias runtest)
(action
(run ./test_bitfield.exe)))

View file

@ -0,0 +1,58 @@
module CG = Containers_codegen
module Vec = CCVector
let spf = Printf.sprintf
let emit_bitfields () =
let module B = CG.Bitfield in
let ml = Vec.create () in
let mli = Vec.create () in
(let b = B.make ~name:"t" () in
B.field_bit b "x";
B.field_bit b "y";
B.field_bit b "z";
B.field_int b ~width:5 "foo";
Vec.push ml (CG.Code.in_struct "T1" [ B.gen_ml b ]);
Vec.push mli (CG.Code.in_sig "T1" [ B.gen_mli b ]);
(* check width *)
Vec.push ml
(CG.Code.mk_str (spf "let() = assert (%d = 8);;" (B.total_width b)));
());
Vec.push ml
@@ CG.Code.mk_str
{|
let n_fails = ref 0;;
at_exit (fun () -> if !n_fails > 0 then exit 1);;
let assert_true line s =
if not s then ( incr n_fails; Printf.eprintf "test failure at %d\n%!" line);;
|};
let test1 =
{|
assert_true __LINE__ T1.(get_y (empty |> set_x true |> set_y true |> set_foo 10));;
assert_true __LINE__ T1.(get_x (empty |> set_x true |> set_y true |> set_foo 10));;
assert_true __LINE__ T1.(get_y (empty |> set_x true |> set_z true
|> set_y false |> set_x false |> set_y true));;
assert_true __LINE__ T1.(get_z (empty |> set_z true));;
assert_true __LINE__ T1.(not @@ get_x (empty |> set_z true));;
assert_true __LINE__ T1.(not @@ get_y (empty |> set_z true |> set_x true));;
assert_true __LINE__ T1.(not @@ get_y (empty |> set_z true |> set_foo 18));;
(* check width of foo *)
assert_true __LINE__ T1.(try ignore (empty |> set_foo (1 lsl 6)); false with _ -> true);;
assert_true __LINE__ T1.(12 = get_foo (empty |> set_x true |> set_foo 12 |> set_x false));;
assert_true __LINE__ T1.(24 = get_foo (empty |> set_y true |> set_foo 24 |> set_z true));;
|}
|> CG.Code.mk_str
in
Vec.push ml test1;
CG.emit_file "test_bitfield.ml" (Vec.to_list ml);
CG.emit_file "test_bitfield.mli" (Vec.to_list mli);
()
let () =
emit_bitfields ();
()

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,11 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Array utils} *) (** Array utils *)
type 'a iter = ('a -> unit) -> unit
(** Fast internal iterator.
@since 2.8 *)
type 'a sequence = ('a -> unit) -> unit
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
type 'a gen = unit -> 'a option type 'a gen = unit -> 'a option
type 'a equal = 'a -> 'a -> bool type 'a equal = 'a -> 'a -> bool
type 'a ord = 'a -> 'a -> int type 'a ord = 'a -> 'a -> int
@ -14,226 +15,293 @@ type 'a printer = Format.formatter -> 'a -> unit
(** {2 Arrays} *) (** {2 Arrays} *)
include module type of Array include module type of Array
(** @inline *)
type 'a t = 'a array
val empty : 'a t val empty : 'a t
(** [empty] is the empty array, physically equal to [[||]]. *)
val equal : 'a equal -> 'a t equal val equal : 'a equal -> 'a t equal
(** [equal eq a1 a2] is [true] if the lengths of [a1] and [a2] are the same
and if their corresponding elements test equal, using [eq]. *)
val compare : 'a ord -> 'a t ord val compare : 'a ord -> 'a t ord
(** [compare cmp a1 a2] compares arrays [a1] and [a2] using the function comparison [cmp]. *)
val swap : 'a t -> int -> int -> unit val swap : 'a t -> int -> int -> unit
(** [swap arr i j] swaps elements at indices [i] and [j]. (** [swap a i j] swaps elements at indices [i] and [j].
@since 1.4 *) @since 1.4 *)
val get : 'a t -> int -> 'a
val get_safe : 'a t -> int -> 'a option val get_safe : 'a t -> int -> 'a option
(** [get_safe a i] returns [Some a.(i)] if [i] is a valid index (** [get_safe a i] returns [Some a.(i)] if [i] is a valid index.
@since 0.18 *) @since 0.18 *)
val set : 'a t -> int -> 'a -> unit val map_inplace : ('a -> 'a) -> 'a t -> unit
(** [map_inplace f a] replace all elements of [a] by its image by [f].
@since 3.8 *)
val length : _ t -> int val mapi_inplace : (int -> 'a -> 'a) -> 'a t -> unit
(** [mapi_inplace f a] replace all elements of [a] by its image by [f].
@since 3.10 *)
val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
(** [fold f init a] computes [f ((f (f init a.(0)) a.(1))) a.(n-1)],
where [n] is the length of the array [a].
Same as {!Array.fold_left}*)
val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a
(** Fold left on array, with index *) (** [foldi f init a] is just like {!fold}, but it also passes in the index
of each element as the second argument to the folded function [f]. *)
val fold_while : ('a -> 'b -> 'a * [`Stop | `Continue]) -> 'a -> 'b t -> 'a val fold_while : ('a -> 'b -> 'a * [ `Stop | `Continue ]) -> 'a -> 'b t -> 'a
(** Fold left on array until a stop condition via [('a, `Stop)] is (** [fold_while f init a] folds left on array [a] until a stop condition via [('a, `Stop)]
indicated by the accumulator is indicated by the accumulator.
@since 0.8 *) @since 0.8 *)
val fold_map : ('acc -> 'a -> 'acc * 'b) -> 'acc -> 'a t -> 'acc * 'b t val fold_map : ('acc -> 'a -> 'acc * 'b) -> 'acc -> 'a t -> 'acc * 'b t
(** [fold_map f acc a] is a [fold_left]-like function, but it also maps the (** [fold_map f init a] is a [fold_left]-like function, but it also maps the
array to another array. array to another array.
@since 1.2 *) @since 1.2, but only
@since 2.1 with labels *)
val scan_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a t -> 'acc t val scan_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a t -> 'acc t
(** [scan_left f acc a] returns the array (** [scan_left f init a] returns the array
[ [|acc; f acc x0; f (f acc a.(0)) a.(1); |] ] [ [|init; f init x0; f (f init a.(0)) a.(1); |] ].
@since 1.2 *)
@since 1.2, but only
val iter : ('a -> unit) -> 'a t -> unit @since 2.1 with labels *)
val iteri : (int -> 'a -> unit) -> 'a t -> unit
val blit : 'a t -> int -> 'a t -> int -> int -> unit
(** [blit from i into j len] copies [len] elements from the first array
to the second. See {!Array.blit}. *)
val reverse_in_place : 'a t -> unit val reverse_in_place : 'a t -> unit
(** Reverse the array in place *) (** [reverse_in_place a] reverses the array [a] in place. *)
val sorted : ('a -> 'a -> int) -> 'a t -> 'a array val sorted : ('a -> 'a -> int) -> 'a t -> 'a array
(** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. (** [sorted f a] makes a copy of [a] and sorts it with [f].
@since 1.0 *) @since 1.0 *)
val sort_indices : ('a -> 'a -> int) -> 'a t -> int array val sort_indices : ('a -> 'a -> int) -> 'a t -> int array
(** [sort_indices cmp a] returns a new array [b], with the same length as [a], (** [sort_indices f a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index at which the [i]-th element of [sorted cmp a] such that [b.(i)] is the index at which the [i]-th element of [sorted f a]
appears in [a]. [a] is not modified. appears in [a]. [a] is not modified.
In other words, [map (fun i -> a.(i)) (sort_indices cmp a) = sorted cmp a]. In other words, [map (fun i -> a.(i)) (sort_indices f a) = sorted f a].
[sort_indices] yields the inverse permutation of {!sort_ranking}. [sort_indices] yields the inverse permutation of {!sort_ranking}.
@since 1.0 *) @since 1.0 *)
val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array
(** [sort_ranking cmp a] returns a new array [b], with the same length as [a], (** [sort_ranking f a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index at which the [i]-the element of [a] appears such that [b.(i)] is the index at which the [i]-th element of [a] appears
in [sorted cmp a]. [a] is not modified. in [sorted f a]. [a] is not modified.
In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. In other words, [map (fun i -> (sorted f a).(i)) (sort_ranking f a) = a].
[sort_ranking] yields the inverse permutation of {!sort_indices}. [sort_ranking] yields the inverse permutation of {!sort_indices}.
In the absence of duplicate elements in [a], we also have In the absence of duplicate elements in [a], we also have
[lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)].
@since 1.0 *) @since 1.0 *)
val mem : ?eq:('a -> 'a -> bool) -> 'a -> 'a t -> bool
(** [mem ~eq x a] return true if x is present in [a]. Linear time.
@since 3.0
*)
val find_map : ('a -> 'b option) -> 'a t -> 'b option val find_map : ('a -> 'b option) -> 'a t -> 'b option
(** [find_map f a] returns [Some y] if there is an element [x] such (** [find_map f a] returns [Some y] if there is an element [x] such
that [f x = Some y], else it returns [None] that [f x = Some y]. Otherwise returns [None].
@since 1.3 @since 1.3, but only
*) @since 2.1 with labels *)
val find : ('a -> 'b option) -> 'a t -> 'b option
(** Alias to {!find_map}
@deprecated since 1.3 *)
val find_map_i : (int -> 'a -> 'b option) -> 'a t -> 'b option val find_map_i : (int -> 'a -> 'b option) -> 'a t -> 'b option
(** Like {!find_map}, but also pass the index to the predicate function. (** [find_map_i f a] is like {!find_map}, but the index of the element is also passed
@since 1.3 *) to the predicate function [f].
@since 1.3, but only
val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option @since 2.1 with labels *)
(** Alias to {!find_map_i}
@since 0.3.4
@deprecated since 1.3 *)
val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option
(** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l], (** [find_idx f a] returns [Some (i,x)] where [x] is the [i]-th element of [a],
and [p x] holds. Otherwise returns [None] and [f x] holds. Otherwise returns [None].
@since 0.3.4 *) @since 0.3.4 *)
val lookup : ?cmp:'a ord -> 'a -> 'a t -> int option val max : ('a -> 'a -> int) -> 'a t -> 'a option
(** Lookup the index of some value in a sorted array. (** [max cmp a] returns [None] if [a] is empty, otherwise, returns [Some e] where [e]
@return [None] if the key is not present, or is a maximum element in [a] with respect to [cmp].
[Some i] ([i] the index of the key) otherwise *) @since 3.12 *)
val lookup_exn : ?cmp:'a ord -> 'a -> 'a t -> int val max_exn : ('a -> 'a -> int) -> 'a t -> 'a
(** Same as {!lookup}, but (** [max_exn cmp a] is like {!max}, but
@raise Not_found if the key is not present *) @raise Invalid_argument if [a] is empty.
@since 3.12 *)
val bsearch : ?cmp:('a -> 'a -> int) -> 'a -> 'a t -> val argmax : ('a -> 'a -> int) -> 'a t -> int option
(** [argmax cmp a] returns [None] if [a] is empty, otherwise, returns [Some i] where [i]
is the index of a maximum element in [a] with respect to [cmp].
@since 3.12 *)
val argmax_exn : ('a -> 'a -> int) -> 'a t -> int
(** [argmax_exn cmp a] is like {!argmax}, but
@raise Invalid_argument if [a] is empty.
@since 3.12 *)
val min : ('a -> 'a -> int) -> 'a t -> 'a option
(** [min cmp a] returns [None] if [a] is empty, otherwise, returns [Some e] where [e]
is a minimum element in [a] with respect to [cmp].
@since 3.12 *)
val min_exn : ('a -> 'a -> int) -> 'a t -> 'a
(** [min_exn cmp a] is like {!min}, but
@raise Invalid_argument if [a] is empty.
@since 3.12 *)
val argmin : ('a -> 'a -> int) -> 'a t -> int option
(** [argmin cmp a] returns [None] if [a] is empty, otherwise, returns [Some i] where [i]
is the index of a minimum element in [a] with respect to [cmp].
@since 3.12 *)
val argmin_exn : ('a -> 'a -> int) -> 'a t -> int
(** [argmin_exn cmp a] is like {!argmin}, but
@raise Invalid_argument if [a] is empty.
@since 3.12 *)
val lookup : cmp:'a ord -> 'a -> 'a t -> int option
(** [lookup ~cmp key a] lookups the index of some key [key] in a sorted array [a].
Undefined behavior if the array [a] is not sorted wrt [~cmp].
Complexity: [O(log (n))] (dichotomic search).
@return [None] if the key [key] is not present, or
[Some i] ([i] the index of the key) otherwise. *)
val lookup_exn : cmp:'a ord -> 'a -> 'a t -> int
(** [lookup_exn ~cmp key a] is like {!lookup}, but
@raise Not_found if the key [key] is not present. *)
val bsearch :
cmp:('a -> 'a -> int) ->
'a ->
'a t ->
[ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ] [ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ]
(** [bsearch ?cmp x arr] finds the index of the object [x] in the array [arr], (** [bsearch ~cmp key a] finds the index of the object [key] in the array [a],
provided [arr] is {b sorted} using [cmp]. If the array is not sorted, provided [a] is {b sorted} using [cmp]. If the array is not sorted,
the result is not specified (may raise Invalid_argument). the result is not specified (may raise Invalid_argument).
Complexity: O(log n) where n is the length of the array Complexity: [O(log n)] where n is the length of the array [a]
(dichotomic search). (dichotomic search).
@return @return
- [`At i] if [cmp arr.(i) x = 0] (for some i) - [`At i] if [cmp a.(i) key = 0] (for some i).
- [`All_lower] if all elements of [arr] are lower than [x] - [`All_lower] if all elements of [a] are lower than [key].
- [`All_bigger] if all elements of [arr] are bigger than [x] - [`All_bigger] if all elements of [a] are bigger than [key].
- [`Just_after i] if [arr.(i) < x < arr.(i+1)] - [`Just_after i] if [a.(i) < key < a.(i+1)].
- [`Empty] if the array is empty - [`Empty] if the array [a] is empty.
@raise Invalid_argument if the array is found to be unsorted w.r.t [cmp] @raise Invalid_argument if the array is found to be unsorted w.r.t [cmp].
@since 0.13 *) @since 0.13 *)
val for_all : ('a -> bool) -> 'a t -> bool
val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** Forall on pairs of arrays. (** [for_all2 f [|a1; …; an|] [|b1; …; bn|]] is [true] if each pair of elements [ai bi]
@raise Invalid_argument if they have distinct lengths satisfies the predicate [f].
allow different types @since 0.20 *) That is, it returns [(f a1 b1) && (f a2 b2) && && (f an bn)].
val exists : ('a -> bool) -> 'a t -> bool @raise Invalid_argument if arrays have distinct lengths.
Allow different types.
val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** Exists on pairs of arrays.
@raise Invalid_argument if they have distinct lengths
allow different types @since 0.20 *)
val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc
(** Fold on two arrays stepwise.
@raise Invalid_argument if they have distinct lengths
@since 0.20 *) @since 0.20 *)
val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** Iterate on two arrays stepwise. (** [exists2 f [|a1; …; an|] [|b1; …; bn|]] is [true] if any pair of elements [ai bi]
@raise Invalid_argument if they have distinct lengths satisfies the predicate [f].
That is, it returns [(f a1 b1) || (f a2 b2) || || (f an bn)].
@raise Invalid_argument if arrays have distinct lengths.
Allow different types.
@since 0.20 *)
val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc
(** [fold2 f init a b] fold on two arrays [a] and [b] stepwise.
It computes [f ( (f init a1 b1) ) an bn].
@raise Invalid_argument if [a] and [b] have distinct lengths.
@since 0.20 *) @since 0.20 *)
val shuffle : 'a t -> unit val shuffle : 'a t -> unit
(** Shuffle randomly the array, in place *) (** [shuffle a] randomly shuffles the array [a], in place. *)
val shuffle_with : Random.State.t -> 'a t -> unit val shuffle_with : Random.State.t -> 'a t -> unit
(** Like shuffle but using a specialized random state *) (** [shuffle_with rs a] randomly shuffles the array [a] (like {!shuffle}) but a specialized random
state [rs] is used to control the random numbers being produced during shuffling (for reproducibility). *)
val random_choose : 'a t -> 'a random_gen val random_choose : 'a t -> 'a random_gen
(** Choose an element randomly. (** [random_choose a rs] randomly chooses an element of [a].
@raise Not_found if the array/slice is empty *) @raise Not_found if the array/slice is empty. *)
val to_string : ?sep:string -> ('a -> string) -> 'a array -> string
(** [to_string ~sep item_to_string a] print [a] to a string using [sep] as a separator
between elements of [a].
@since 2.7 *)
val to_iter : 'a t -> 'a iter
(** [to_iter a] returns an [iter] of the elements of an array [a].
The input array [a] is shared with the sequence and modification of it will result
in modification of the iterator.
@since 2.8 *)
val to_seq : 'a t -> 'a Seq.t
(** [to_seq a] returns a [Seq.t] of the elements of an array [a].
The input array [a] is shared with the sequence and modification of it will result
in modification of the sequence.
Renamed from [to_std_seq] since 3.0.
@since 3.0
*)
val to_seq : 'a t -> 'a sequence
val to_gen : 'a t -> 'a gen val to_gen : 'a t -> 'a gen
val to_klist : 'a t -> 'a klist (** [to_gen a] returns a [gen] of the elements of an array [a]. *)
(** {2 IO} *) (** {2 IO} *)
val pp: ?sep:string -> 'a printer -> 'a t printer val pp :
(** Print an array of items with printing function *) ?pp_start:unit printer ->
?pp_stop:unit printer ->
?pp_sep:unit printer ->
'a printer ->
'a t printer
(** [pp ~pp_start ~pp_stop ~pp_sep pp_item ppf a] formats the array [a] on [ppf].
Each element is formatted with [pp_item], [pp_start] is called at the beginning,
[pp_stop] is called at the end, [pp_sep] is called between each elements.
By defaults [pp_start] and [pp_stop] does nothing and [pp_sep] defaults to
(fun out -> Format.fprintf out ",@ "). *)
val pp_i: ?sep:string -> (int -> 'a printer) -> 'a t printer val pp_i :
(** Print an array, giving the printing function both index and item *) ?pp_start:unit printer ->
?pp_stop:unit printer ->
val map : ('a -> 'b) -> 'a t -> 'b t ?pp_sep:unit printer ->
(int -> 'a printer) ->
val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t 'a t printer
(** Map on two arrays stepwise. (** [pp_i ~pp_start ~pp_stop ~pp_sep pp_item ppf a] prints the array [a] on [ppf].
@raise Invalid_argument if they have distinct lengths The printing function [pp_item] is giving both index and element.
@since 0.20 *) [pp_start] is called at the beginning,
[pp_stop] is called at the end, [pp_sep] is called between each elements.
By defaults [pp_start] and [pp_stop] does nothing and [pp_sep] defaults to
(fun out -> Format.fprintf out ",@ "). *)
val rev : 'a t -> 'a t val rev : 'a t -> 'a t
(** Copy + reverse in place (** [rev a] copies the array [a] and reverses it in place.
@since 0.20 *) @since 0.20 *)
val filter : ('a -> bool) -> 'a t -> 'a t val filter : ('a -> bool) -> 'a t -> 'a t
(** Filter elements out of the array. Only the elements satisfying (** [filter f a] filters elements out of the array [a]. Only the elements satisfying
the given predicate will be kept. *) the given predicate [f] will be kept. *)
val filter_map : ('a -> 'b option) -> 'a t -> 'b t val filter_map : ('a -> 'b option) -> 'a t -> 'b t
(** Map each element into another value, or discard it *) (** [filter_map f [|a1; …; an|]] calls [(f a1)(f an)] and returns an array [b] consisting
of all elements [bi] such as [f ai = Some bi]. When [f] returns [None], the corresponding
element of [a] is discarded. *)
val monoid_product : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
(** [monoid_product f a b] passes all combinaisons of tuples from the two arrays [a] and [b]
to the function [f].
@since 2.8 *)
val flat_map : ('a -> 'b t) -> 'a t -> 'b array val flat_map : ('a -> 'b t) -> 'a t -> 'b array
(** Transform each element into an array, then flatten *) (** [flat_map f a] transforms each element of [a] into an array, then flattens. *)
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
(** Infix version of {!flat_map} *)
val (>>|) : 'a t -> ('a -> 'b) -> 'b t
(** Infix version of {!map}
@since 0.8 *)
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
(** Infix version of {!map}
@since 0.8 *)
val except_idx : 'a t -> int -> 'a list val except_idx : 'a t -> int -> 'a list
(** Remove given index, obtaining the list of the other elements *) (** [except_idx a i] removes the element of [a] at given index [i], and returns
the list of the other elements. *)
val (--) : int -> int -> int t
(** Range array *)
val (--^) : int -> int -> int t
(** Range array, excluding right bound
@since 0.17 *)
val random : 'a random_gen -> 'a t random_gen val random : 'a random_gen -> 'a t random_gen
val random_non_empty : 'a random_gen -> 'a t random_gen val random_non_empty : 'a random_gen -> 'a t random_gen
@ -246,15 +314,48 @@ module type MONO_ARRAY = sig
type t type t
val length : t -> int val length : t -> int
val get : t -> int -> elt val get : t -> int -> elt
val set : t -> int -> elt -> unit val set : t -> int -> elt -> unit
end end
val sort_generic : val sort_generic :
(module MONO_ARRAY with type t = 'arr and type elt = 'elt) -> (module MONO_ARRAY with type t = 'arr and type elt = 'elt) ->
?cmp:('elt -> 'elt -> int) -> 'arr -> unit cmp:('elt -> 'elt -> int) ->
(** Sort the array, without allocating (eats stack space though). Performance 'arr ->
might be lower than {!Array.sort}. unit
(** [sort_generic (module M) ~cmp a] sorts the array [a], without allocating (eats stack space though).
Performance might be lower than {!Array.sort}.
@since 0.14 *) @since 0.14 *)
(** {3 Infix Operators}
It is convenient to [open CCArray.Infix] to access the infix operators
without cluttering the scope too much.
@since 2.7 *)
module Infix : sig
val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
(** [a >>= f] is the infix version of {!flat_map}. *)
val ( >>| ) : 'a t -> ('a -> 'b) -> 'b t
(** [a >>| f] is the infix version of {!map}.
@since 0.8 *)
val ( >|= ) : 'a t -> ('a -> 'b) -> 'b t
(** [a >|= f] is the infix version of {!map}.
@since 0.8 *)
val ( -- ) : int -> int -> int t
(** [x -- y] creates an array containing integers in the range [x .. y]. Bounds included. *)
val ( --^ ) : int -> int -> int t
(** [x --^ y] creates an array containing integers in the range [x .. y]. Right bound excluded.
@since 0.17 *)
val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t
val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
val ( and* ) : 'a t -> 'b t -> ('a * 'b) t
end
include module type of Infix

View file

@ -1,4 +1,3 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
include CCArray include CCArray

View file

@ -1,10 +1,11 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Array utils} *) (** Array utils (Labeled version of {!CCArray}) *)
type 'a iter = ('a -> unit) -> unit
(** Fast internal iterator.
@since 2.8 *)
type 'a sequence = ('a -> unit) -> unit
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
type 'a gen = unit -> 'a option type 'a gen = unit -> 'a option
type 'a equal = 'a -> 'a -> bool type 'a equal = 'a -> 'a -> bool
type 'a ord = 'a -> 'a -> int type 'a ord = 'a -> 'a -> int
@ -13,198 +14,310 @@ type 'a printer = Format.formatter -> 'a -> unit
(** {2 Arrays} *) (** {2 Arrays} *)
include module type of ArrayLabels include module type of ArrayLabels with module Floatarray = Array.Floatarray
(** @inline *)
type 'a t = 'a array
val empty : 'a t val empty : 'a t
(** [empty] is the empty array, physically equal to [[||]]. *)
val equal : 'a equal -> 'a t equal val equal : 'a equal -> 'a t equal
(** [equal eq a1 a2] is [true] if the lengths of [a1] and [a2] are the same
and if their corresponding elements test equal, using [eq]. *)
val compare : 'a ord -> 'a t ord val compare : 'a ord -> 'a t ord
(** [compare cmp a1 a2] compares arrays [a1] and [a2] using the function comparison [cmp]. *)
val get : 'a t -> int -> 'a val swap : 'a t -> int -> int -> unit
(** [swap a i j] swaps elements at indices [i] and [j].
@since 1.4 *)
val get_safe : 'a t -> int -> 'a option val get_safe : 'a t -> int -> 'a option
(** [get_safe a i] returns [Some a.(i)] if [i] is a valid index (** [get_safe a i] returns [Some a.(i)] if [i] is a valid index.
@since 0.18 *) @since 0.18 *)
val set : 'a t -> int -> 'a -> unit val map_inplace : f:('a -> 'a) -> 'a t -> unit
(** [map_inplace ~f a] replace all elements of [a] by its image by [f].
@since 3.8 *)
val length : _ t -> int val mapi_inplace : f:(int -> 'a -> 'a) -> 'a t -> unit
(** [mapi_inplace ~f a] replace all elements of [a] by its image by [f].
@since 3.10 *)
val fold : f:('a -> 'b -> 'a) -> init:'a -> 'b t -> 'a val fold : f:('a -> 'b -> 'a) -> init:'a -> 'b t -> 'a
(** [fold ~f ~init a] computes [f ((f (f init a.(0)) a.(1))) a.(n-1)],
where [n] is the length of the array [a].
Same as {!ArrayLabels.fold_left} *)
val foldi : f:('a -> int -> 'b -> 'a) -> init:'a -> 'b t -> 'a val foldi : f:('a -> int -> 'b -> 'a) -> init:'a -> 'b t -> 'a
(** Fold left on array, with index *) (** [foldi ~f ~init a] is just like {!fold}, but it also passes in the index
of each element as the second argument to the folded function [f]. *)
val fold_while : f:('a -> 'b -> 'a * [`Stop | `Continue]) -> init:'a -> 'b t -> 'a val fold_while :
(** Fold left on array until a stop condition via [('a, `Stop)] is f:('a -> 'b -> 'a * [ `Stop | `Continue ]) -> init:'a -> 'b t -> 'a
indicated by the accumulator (** [fold_while ~f ~init a] folds left on array [a] until a stop condition via [('a, `Stop)]
is indicated by the accumulator.
@since 0.8 *) @since 0.8 *)
val iter : f:('a -> unit) -> 'a t -> unit val fold_map : f:('acc -> 'a -> 'acc * 'b) -> init:'acc -> 'a t -> 'acc * 'b t
(** [fold_map ~f ~init a] is a [fold_left]-like function, but it also maps the
array to another array.
@since 1.2, but only
@since 2.1 with labels *)
val iteri : f:(int -> 'a -> unit) -> 'a t -> unit val scan_left : f:('acc -> 'a -> 'acc) -> init:'acc -> 'a t -> 'acc t
(** [scan_left ~f ~init a] returns the array
[ [|init; f init x0; f (f init a.(0)) a.(1); |] ].
val blit : 'a t -> int -> 'a t -> int -> int -> unit @since 1.2, but only
(** [blit from i into j len] copies [len] elements from the first array @since 2.1 with labels *)
to the second. See {!Array.blit}. *)
val reverse_in_place : 'a t -> unit val reverse_in_place : 'a t -> unit
(** Reverse the array in place *) (** [reverse_in_place a] reverses the array [a] in place. *)
val sorted : f:('a -> 'a -> int) -> 'a t -> 'a array val sorted : f:('a -> 'a -> int) -> 'a t -> 'a array
(** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. (** [sorted ~f a] makes a copy of [a] and sorts it with [f].
@since 1.0 *) @since 1.0 *)
val sort_indices : f:('a -> 'a -> int) -> 'a t -> int array val sort_indices : f:('a -> 'a -> int) -> 'a t -> int array
(** [sort_indices cmp a] returns a new array [b], with the same length as [a], (** [sort_indices ~f a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index of the [i]-th element of [a] in [sort cmp a]. such that [b.(i)] is the index at which the [i]-th element of [sorted f a]
In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. appears in [a]. [a] is not modified.
[a] is not modified.
In other words, [map (fun i -> a.(i)) (sort_indices f a) = sorted f a].
[sort_indices] yields the inverse permutation of {!sort_ranking}.
@since 1.0 *) @since 1.0 *)
val sort_ranking : f:('a -> 'a -> int) -> 'a t -> int array val sort_ranking : f:('a -> 'a -> int) -> 'a t -> int array
(** [sort_ranking cmp a] returns a new array [b], with the same length as [a], (** [sort_ranking ~f a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the position in [sorted cmp a] of the [i]-th such that [b.(i)] is the index at which the [i]-th element of [a] appears
element of [a]. in [sorted f a]. [a] is not modified.
[a] is not modified.
In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. In other words, [map (fun i -> (sorted f a).(i)) (sort_ranking f a) = a].
[sort_ranking] yields the inverse permutation of {!sort_indices}.
Without duplicates, we also have In the absence of duplicate elements in [a], we also have
[lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)].
@since 1.0 *) @since 1.0 *)
val find : f:('a -> 'b option) -> 'a t -> 'b option val mem : ?eq:('a -> 'a -> bool) -> 'a -> 'a t -> bool
(** [find f a] returns [Some y] if there is an element [x] such (** [mem ~eq x a] return true if x is present in [a]. Linear time.
that [f x = Some y], else it returns [None] *) @since 3.0
*)
val findi : f:(int -> 'a -> 'b option) -> 'a t -> 'b option val find_map : f:('a -> 'b option) -> 'a t -> 'b option
(** Like {!find}, but also pass the index to the predicate function. (** [find_map ~f a] returns [Some y] if there is an element [x] such
@since 0.3.4 *) that [f x = Some y]. Otherwise returns [None].
@since 1.3, but only
@since 2.1 with labels *)
val find_map_i : f:(int -> 'a -> 'b option) -> 'a t -> 'b option
(** [find_map_i ~f a] is like {!find_map}, but the index of the element is also passed
to the predicate function [f].
@since 1.3, but only
@since 2.1 with labels *)
val find_idx : f:('a -> bool) -> 'a t -> (int * 'a) option val find_idx : f:('a -> bool) -> 'a t -> (int * 'a) option
(** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l], (** [find_idx ~f a] returns [Some (i,x)] where [x] is the [i]-th element of [a],
and [p x] holds. Otherwise returns [None] and [f x] holds. Otherwise returns [None].
@since 0.3.4 *) @since 0.3.4 *)
val lookup : ?cmp:'a ord -> key:'a -> 'a t -> int option val max : cmp:('a -> 'a -> int) -> 'a t -> 'a option
(** Lookup the index of some value in a sorted array. (** [max ~cmp a] returns [None] if [a] is empty, otherwise, returns [Some e] where [e]
@return [None] if the key is not present, or is a maximum element in [a] with respect to [cmp].
[Some i] ([i] the index of the key) otherwise *) @since 3.12 *)
val lookup_exn : ?cmp:'a ord -> key:'a -> 'a t -> int val max_exn : cmp:('a -> 'a -> int) -> 'a t -> 'a
(** Same as {!lookup_exn}, but (** [max_exn ~cmp a] is like {!max}, but
@raise Not_found if the key is not present *) @raise Invalid_argument if [a] is empty.
@since 3.12 *)
val bsearch : ?cmp:('a -> 'a -> int) -> key:'a -> 'a t -> val argmax : cmp:('a -> 'a -> int) -> 'a t -> int option
(** [argmax ~cmp a] returns [None] if [a] is empty, otherwise, returns [Some i] where [i]
is the index of a maximum element in [a] with respect to [cmp].
@since 3.12 *)
val argmax_exn : cmp:('a -> 'a -> int) -> 'a t -> int
(** [argmax_exn ~cmp a] is like {!argmax}, but
@raise Invalid_argument if [a] is empty.
@since 3.12 *)
val min : cmp:('a -> 'a -> int) -> 'a t -> 'a option
(** [min ~cmp a] returns [None] if [a] is empty, otherwise, returns [Some e] where [e]
is a minimum element in [a] with respect to [cmp].
@since 3.12 *)
val min_exn : cmp:('a -> 'a -> int) -> 'a t -> 'a
(** [min_exn ~cmp a] is like {!min}, but
@raise Invalid_argument if [a] is empty.
@since 3.12 *)
val argmin : cmp:('a -> 'a -> int) -> 'a t -> int option
(** [argmin ~cmp a] returns [None] if [a] is empty, otherwise, returns [Some i] where [i]
is the index of a minimum element in [a] with respect to [cmp].
@since 3.12 *)
val argmin_exn : cmp:('a -> 'a -> int) -> 'a t -> int
(** [argmin_exn ~cmp a] is like {!argmin}, but
@raise Invalid_argument if [a] is empty.
@since 3.12 *)
val lookup : cmp:('a ord[@keep_label]) -> key:'a -> 'a t -> int option
(** [lookup ~cmp ~key a] lookups the index of some key [key] in a sorted array [a].
Undefined behavior if the array [a] is not sorted wrt [cmp].
Complexity: [O(log (n))] (dichotomic search).
@return [None] if the key [key] is not present, or
[Some i] ([i] the index of the key) otherwise. *)
val lookup_exn : cmp:('a ord[@keep_label]) -> key:'a -> 'a t -> int
(** [lookup_exn ~cmp ~key a] is like {!lookup}, but
@raise Not_found if the key [key] is not present. *)
val bsearch :
cmp:(('a -> 'a -> int)[@keep_label]) ->
key:'a ->
'a t ->
[ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ] [ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ]
(** [bsearch ?cmp key arr] finds the index of the object [key] in the array [arr], (** [bsearch ~cmp ~key a] finds the index of the object [key] in the array [a],
provided [arr] is {b sorted} using [cmp]. If the array is not sorted, provided [a] is {b sorted} using [cmp]. If the array is not sorted,
the result is not specified (may raise Invalid_argument). the result is not specified (may raise Invalid_argument).
Complexity: O(log n) where n is the length of the array Complexity: [O(log n)] where n is the length of the array [a]
(dichotomic search). (dichotomic search).
@return @return
- [`At i] if [cmp arr.(i) key = 0] (for some i) - [`At i] if [cmp a.(i) key = 0] (for some i).
- [`All_lower] if all elements of [arr] are lower than [key] - [`All_lower] if all elements of [a] are lower than [key].
- [`All_bigger] if all elements of [arr] are bigger than [key] - [`All_bigger] if all elements of [a] are bigger than [key].
- [`Just_after i] if [arr.(i) < key < arr.(i+1)] - [`Just_after i] if [a.(i) < key < a.(i+1)].
- [`Empty] if the array is empty - [`Empty] if the array [a] is empty.
@raise Invalid_argument if the array is found to be unsorted w.r.t [cmp] @raise Invalid_argument if the array is found to be unsorted w.r.t [cmp].
@since 0.13 *) @since 0.13 *)
val for_all : f:('a -> bool) -> 'a t -> bool
val for_all2 : f:('a -> 'b -> bool) -> 'a t -> 'b t -> bool val for_all2 : f:('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** Forall on pairs of arrays. (** [for_all2 ~f [|a1; …; an|] [|b1; …; bn|]] is [true] if each pair of elements [ai bi]
@raise Invalid_argument if they have distinct lengths satisfies the predicate [f].
allow different types @since 0.20 *) That is, it returns [(f a1 b1) && (f a2 b2) && && (f an bn)].
val exists : f:('a -> bool) -> 'a t -> bool @raise Invalid_argument if arrays have distinct lengths.
Allow different types.
@since 0.20 *)
val exists2 : f:('a -> 'b -> bool) -> 'a t -> 'b t -> bool val exists2 : f:('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** Exists on pairs of arrays. (** [exists2 ~f [|a1; …; an|] [|b1; …; bn|]] is [true] if any pair of elements [ai bi]
@raise Invalid_argument if they have distinct lengths satisfies the predicate [f].
allow different types @since 0.20 *) That is, it returns [(f a1 b1) || (f a2 b2) || || (f an bn)].
@raise Invalid_argument if arrays have distinct lengths.
Allow different types.
@since 0.20 *)
val fold2 : f:('acc -> 'a -> 'b -> 'acc) -> init:'acc -> 'a t -> 'b t -> 'acc val fold2 : f:('acc -> 'a -> 'b -> 'acc) -> init:'acc -> 'a t -> 'b t -> 'acc
(** Fold on two arrays stepwise. (** [fold2 ~f ~init a b] fold on two arrays [a] and [b] stepwise.
@raise Invalid_argument if they have distinct lengths It computes [f ( (f init a1 b1) ) an bn].
@raise Invalid_argument if [a] and [b] have distinct lengths.
@since 0.20 *) @since 0.20 *)
val iter2 : f:('a -> 'b -> unit) -> 'a t -> 'b t -> unit val iter2 : f:('a -> 'b -> unit) -> 'a t -> 'b t -> unit
(** Iterate on two arrays stepwise. (** [iter2 ~f a b] iterates on the two arrays [a] and [b] stepwise.
@raise Invalid_argument if they have distinct lengths It is equivalent to [f a0 b0; ; f a.(length a - 1) b.(length b - 1); ()].
@raise Invalid_argument if [a] and [b] have distinct lengths.
@since 0.20 *) @since 0.20 *)
val shuffle : 'a t -> unit val shuffle : 'a t -> unit
(** Shuffle randomly the array, in place *) (** [shuffle a] randomly shuffles the array [a], in place. *)
val shuffle_with : Random.State.t -> 'a t -> unit val shuffle_with : Random.State.t -> 'a t -> unit
(** Like shuffle but using a specialized random state *) (** [shuffle_with rs a] randomly shuffles the array [a] (like {!shuffle}) but a specialized random
state [rs] is used to control the random numbers being produced during shuffling (for reproducibility). *)
val random_choose : 'a t -> 'a random_gen val random_choose : 'a t -> 'a random_gen
(** Choose an element randomly. (** [random_choose a rs] randomly chooses an element of [a].
@raise Not_found if the array/slice is empty *) @raise Not_found if the array/slice is empty. *)
val to_string : ?sep:string -> ('a -> string) -> 'a array -> string
(** [to_string ~sep item_to_string a] print [a] to a string using [sep] as a separator
between elements of [a].
@since 2.7 *)
val to_iter : 'a t -> 'a iter
(** [to_iter a] returns an [iter] of the elements of an array [a].
The input array [a] is shared with the sequence and modification of it will result
in modification of the iterator.
@since 2.8 *)
val to_seq : 'a t -> 'a Seq.t
(** [to_seq a] returns a [Seq.t] of the elements of an array [a].
The input array [a] is shared with the sequence and modification of it will result
in modification of the sequence.
Renamed from [to_std_seq] since 3.0.
@since 3.0
*)
val to_seq : 'a t -> 'a sequence
val to_gen : 'a t -> 'a gen val to_gen : 'a t -> 'a gen
val to_klist : 'a t -> 'a klist (** [to_gen a] returns a [gen] of the elements of an array [a]. *)
(** {2 IO} *) (** {2 IO} *)
val pp: ?sep:string -> 'a printer -> 'a t printer val pp :
(** Print an array of items with printing function *) ?pp_start:unit printer ->
?pp_stop:unit printer ->
?pp_sep:unit printer ->
'a printer ->
'a t printer
(** [pp ~pp_start ~pp_stop ~pp_sep pp_item ppf a] formats the array [a] on [ppf].
Each element is formatted with [pp_item], [pp_start] is called at the beginning,
[pp_stop] is called at the end, [pp_sep] is called between each elements.
By defaults [pp_start] and [pp_stop] does nothing and [pp_sep] defaults to
(fun out -> Format.fprintf out ",@ "). *)
val pp_i: ?sep:string -> (int -> 'a printer) -> 'a t printer val pp_i :
(** Print an array, giving the printing function both index and item *) ?pp_start:unit printer ->
?pp_stop:unit printer ->
val map : f:('a -> 'b) -> 'a t -> 'b t ?pp_sep:unit printer ->
(int -> 'a printer) ->
'a t printer
(** [pp_i ~pp_start ~pp_stop ~pp_sep pp_item ppf a] prints the array [a] on [ppf].
The printing function [pp_item] is giving both index and element.
[pp_start] is called at the beginning,
[pp_stop] is called at the end, [pp_sep] is called between each elements.
By defaults [pp_start] and [pp_stop] does nothing and [pp_sep] defaults to
(fun out -> Format.fprintf out ",@ "). *)
val map2 : f:('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t val map2 : f:('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
(** Map on two arrays stepwise. (** [map2 ~f a b] applies function [f] to all elements of [a] and [b],
@raise Invalid_argument if they have distinct lengths and builds an array with the results returned by [f]:
@since 0.20 *) [[| f a.(0) b.(0); ; f a.(length a - 1) b.(length b - 1)|]].
@raise Invalid_argument if [a] and [b] have distinct lengths.
@since 0.20 *)
val rev : 'a t -> 'a t val rev : 'a t -> 'a t
(** Copy + reverse in place (** [rev a] copies the array [a] and reverses it in place.
@since 0.20 *) @since 0.20 *)
val filter : f:('a -> bool) -> 'a t -> 'a t val filter : f:('a -> bool) -> 'a t -> 'a t
(** Filter elements out of the array. Only the elements satisfying (** [filter ~f a] filters elements out of the array [a]. Only the elements satisfying
the given predicate will be kept. *) the given predicate [f] will be kept. *)
val filter_map : f:('a -> 'b option) -> 'a t -> 'b t val filter_map : f:('a -> 'b option) -> 'a t -> 'b t
(** Map each element into another value, or discard it *) (** [filter_map ~f [|a1; …; an|]] calls [(f a1)(f an)] and returns an array [b] consisting
of all elements [bi] such as [f ai = Some bi]. When [f] returns [None], the corresponding
element of [a] is discarded. *)
val monoid_product : f:('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
(** [monoid_product ~f a b] passes all combinaisons of tuples from the two arrays [a] and [b]
to the function [f].
@since 2.8 *)
val flat_map : f:('a -> 'b t) -> 'a t -> 'b array val flat_map : f:('a -> 'b t) -> 'a t -> 'b array
(** Transform each element into an array, then flatten *) (** [flat_map ~f a] transforms each element of [a] into an array, then flattens. *)
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
(** Infix version of {!flat_map} *)
val (>>|) : 'a t -> ('a -> 'b) -> 'b t
(** Infix version of {!map}
@since 0.8 *)
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
(** Infix version of {!map}
@since 0.8 *)
val except_idx : 'a t -> int -> 'a list val except_idx : 'a t -> int -> 'a list
(** Remove given index, obtaining the list of the other elements *) (** [except_idx a i] removes the element of [a] at given index [i], and returns
the list of the other elements. *)
val (--) : int -> int -> int t
(** Range array *)
val (--^) : int -> int -> int t
(** Range array, excluding right bound
@since 0.17 *)
val random : 'a random_gen -> 'a t random_gen val random : 'a random_gen -> 'a t random_gen
val random_non_empty : 'a random_gen -> 'a t random_gen val random_non_empty : 'a random_gen -> 'a t random_gen
@ -217,15 +330,48 @@ module type MONO_ARRAY = sig
type t type t
val length : t -> int val length : t -> int
val get : t -> int -> elt val get : t -> int -> elt
val set : t -> int -> elt -> unit val set : t -> int -> elt -> unit
end end
val sort_generic : val sort_generic :
(module MONO_ARRAY with type t = 'arr and type elt = 'elt) -> (module MONO_ARRAY with type t = 'arr and type elt = 'elt) ->
?cmp:('elt -> 'elt -> int) -> 'arr -> unit cmp:(('elt -> 'elt -> int)[@keep_label]) ->
(** Sort the array, without allocating (eats stack space though). Performance 'arr ->
might be lower than {!Array.sort}. unit
(** [sort_generic (module M) ~cmp a] sorts the array [a], without allocating (eats stack space though).
Performance might be lower than {!Array.sort}.
@since 0.14 *) @since 0.14 *)
(** {3 Infix Operators}
It is convenient to [open CCArray.Infix] to access the infix operators
without cluttering the scope too much.
@since 2.7 *)
module Infix : sig
val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
(** [a >>= f] is the infix version of {!flat_map}. *)
val ( >>| ) : 'a t -> ('a -> 'b) -> 'b t
(** [a >>| f] is the infix version of {!map}.
@since 0.8 *)
val ( >|= ) : 'a t -> ('a -> 'b) -> 'b t
(** [a >|= f] is the infix version of {!map}.
@since 0.8 *)
val ( -- ) : int -> int -> int t
(** [x -- y] creates an array containing integers in the range [x .. y]. Bounds included. *)
val ( --^ ) : int -> int -> int t
(** [x --^ y] creates an array containing integers in the range [x .. y]. Right bound excluded.
@since 0.17 *)
val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t
val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
val ( and* ) : 'a t -> 'b t -> ('a * 'b) t
end
include module type of Infix

View file

@ -1,418 +0,0 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Array Slice} *)
type 'a sequence = ('a -> unit) -> unit
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
type 'a gen = unit -> 'a option
type 'a equal = 'a -> 'a -> bool
type 'a ord = 'a -> 'a -> int
type 'a random_gen = Random.State.t -> 'a
type 'a printer = Format.formatter -> 'a -> unit
(*$inject
let (--) = CCArray.(--)
*)
type 'a t = {
arr : 'a array;
i : int; (** Start index (included) *)
j : int; (** Stop index (excluded) *)
}
let empty = {
arr = [||];
i = 0;
j = 0;
}
let make arr i ~len =
if i<0||i+len > Array.length arr then invalid_arg "Array_slice.make";
{ arr; i; j=i+len; }
let of_slice (arr,i,len) = make arr i ~len
let to_slice a = a.arr, a.i, a.j-a.i
let full arr = { arr; i=0; j=Array.length arr; }
let underlying a = a.arr
let length a = a.j - a.i
let copy a = Array.sub a.arr a.i (length a)
let sub a i len = make a.arr (a.i + i) ~len
(*$=
[ 3;4 ] \
(let a = make (0--10) 2 5 in sub a 1 2 |> to_list)
[ ] \
(let a = make (0--10) 2 5 in sub a 1 0 |> to_list)
[ 5 ] \
(let a = make (0--10) 1 9 in sub a 4 1 |> to_list)
*)
let rec _foldi f acc a i j =
if i = j then acc else _foldi f (f acc i a.(i)) a (i+1) j
let _reverse_in_place a i ~len =
if len=0 then ()
else
for k = 0 to (len-1)/2 do
let t = a.(i+k) in
a.(i+k) <- a.(i+len-1-k);
a.(i+len-1-k) <- t;
done
let rec _equal eq a1 i1 j1 a2 i2 j2 =
if i1 = j1
then (assert (i1=j1 && i2=j2); true)
else
eq a1.(i1) a2.(i2) && _equal eq a1 (i1+1) j1 a2 (i2+1) j2
let rec _compare cmp a1 i1 j1 a2 i2 j2 =
if i1 = j1
then if i2=j2 then 0 else -1
else if i2=j2
then 1
else
let c = cmp a1.(i1) a2.(i2) in
if c = 0
then _compare cmp a1 (i1+1) j1 a2 (i2+1) j2
else c
let equal eq a b =
length a = length b && _equal eq a.arr a.i a.j b.arr b.i b.j
let compare cmp a b =
_compare cmp a.arr a.i a.j b.arr b.i b.j
let fold f acc a =
let rec _fold acc i j =
if i=j then acc
else _fold (f acc a.arr.(i)) (i+1) j
in _fold acc a.i a.j
let to_list a =
let l = fold (fun l x -> x::l) [] a in
List.rev l
let foldi f acc a = _foldi f acc a.arr a.i a.j
let fold_while f acc a =
let rec fold_while_i f acc i =
if i < Array.length a.arr && i < a.j then
let acc, cont = f acc a.arr.(i) in
match cont with
| `Stop -> acc
| `Continue -> fold_while_i f acc (i+1)
else acc
in fold_while_i f acc a.i
let get a i =
let j = a.i + i in
if i<0 || j>=a.j then invalid_arg "Array_slice.get";
a.arr.(j)
let get_safe a i =
try Some (get a i)
with Invalid_argument _ -> None
(*$inject
let sub_a = make [|1;2;3;4;5|] 1 ~len:3
*)
(*$=
(Some 2) (get_safe sub_a 0)
(Some 3) (get_safe sub_a 1)
(Some 4) (get_safe sub_a 2)
None (get_safe sub_a 4)
None (get_safe sub_a max_int)
None (get_safe sub_a ~-1)
None (get_safe sub_a ~-42)
*)
let set a i x =
let j = a.i + i in
if i<0 || j>=a.j then invalid_arg "Array_slice.set";
a.arr.(j) <- x
let iter f a =
for k=a.i to a.j-1 do f a.arr.(k) done
let iteri f a =
for k=0 to length a-1 do f k a.arr.(a.i + k) done
let blit a i b j len =
if i+len>length a || j+len>length b then invalid_arg "Array_slice.blit";
Array.blit a.arr (a.i+i) b.arr (b.i+j) len
let rec _find f a i j =
if i = j then None
else match f i a.(i) with
| Some _ as res -> res
| None -> _find f a (i+1) j
let rec _lookup_rec ~cmp k a i j =
if i>j then raise Not_found
else if i=j
then if cmp k a.(i) = 0
then i
else raise Not_found
else
let middle = (j+i)/2 in
match cmp k a.(middle) with
| 0 -> middle
| n when n<0 -> _lookup_rec ~cmp k a i (middle-1)
| _ -> _lookup_rec ~cmp k a (middle+1) j
let _lookup_exn ~cmp k a i j =
if i>j then raise Not_found;
match cmp k a.(i) with
| 0 -> i
| n when n<0 -> raise Not_found (* too low *)
| _ when i=j -> raise Not_found (* too high *)
| _ ->
match cmp k a.(j) with
| 0 -> j
| n when n<0 -> _lookup_rec ~cmp k a (i+1) (j-1)
| _ -> raise Not_found (* too high *)
let bsearch_ ~cmp x arr i j =
let rec aux i j =
if i > j
then `Just_after j
else
let middle = i + (j - i) / 2 in (* avoid overflow *)
match cmp x arr.(middle) with
| 0 -> `At middle
| n when n<0 -> aux i (middle - 1)
| _ -> aux (middle + 1) j
in
if i>=j then `Empty
else match cmp arr.(i) x, cmp arr.(j) x with
| n, _ when n>0 -> `All_bigger
| _, n when n<0 -> `All_lower
| _ -> aux i j
let rec _for_all p a i j =
i = j || (p a.(i) && _for_all p a (i+1) j)
let rec _exists p a i j =
i <> j && (p a.(i) || _exists p a (i+1) j)
let rec _for_all2 p a1 a2 i1 i2 ~len =
len=0 || (p a1.(i1) a2.(i2) && _for_all2 p a1 a2 (i1+1) (i2+1) ~len:(len-1))
let rec _exists2 p a1 a2 i1 i2 ~len =
len>0 && (p a1.(i1) a2.(i2) || _exists2 p a1 a2 (i1+1) (i2+1) ~len:(len-1))
(* shuffle a[i...j[ using the given int random generator
See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle *)
let _shuffle _rand_int a i j =
for k = j-1 downto i+1 do
let l = _rand_int (k+1) in
let tmp = a.(l) in
a.(l) <- a.(k);
a.(k) <- tmp;
done
(*$T
let st = Random.State.make [||] in let a = 0--10000 in \
let b = Array.copy a in CCArray.shuffle_with st a; a <> b
*)
let _sort_indices cmp a i j =
let len = j-i in
let b = Array.init len (fun k->k) in
Array.sort (fun k1 k2 -> cmp a.(k1+i) a.(k2+i)) b;
b
let _sorted cmp a i j =
let len = j-i in
let b = Array.sub a i len in
Array.sort cmp b;
b
let _choose a i j st =
if i>=j then raise Not_found;
a.(i+Random.State.int st (j-i))
let _pp ~sep pp_item out a i j =
for k = i to j - 1 do
if k > i then (Format.pp_print_string out sep; Format.pp_print_cut out ());
pp_item out a.(k)
done
let _pp_i ~sep pp_item out a i j =
for k = i to j - 1 do
if k > i then (Format.pp_print_string out sep; Format.pp_print_cut out ());
pp_item k out a.(k)
done
let _to_gen a i j =
let k = ref i in
fun () ->
if !k < j
then (
let x = a.(!k) in
incr k;
Some x
) else None
let rec _to_klist a i j () =
if i=j then `Nil else `Cons (a.(i), _to_klist a (i+1) j)
let reverse_in_place a = _reverse_in_place a.arr a.i ~len:(length a)
(*$T
let a = 1--6 in let s = make a 2 ~len:3 in \
reverse_in_place s; a = [| 1; 2; 5; 4; 3; 6 |]
*)
let sorted cmp a = _sorted cmp a.arr a.i a.j
(*$= & ~cmp:(=) ~printer:Q.Print.(array int)
[||] \
(let a = 1--6 in let s = make a 2 ~len:0 in \
sorted Pervasives.compare s)
[|2;3;4|] \
(let a = [|6;5;4;3;2;1|] in let s = make a 2 ~len:3 in \
sorted Pervasives.compare s)
*)
(*$Q
Q.(array int) (fun a -> \
Array.length a > 10 ==> ( Array.length a > 10 && \
let s = make a 5 ~len:5 in \
let b = Array.sub a 5 5 in \
Array.sort Pervasives.compare b; b = sorted Pervasives.compare s))
*)
let sort_ranking cmp a =
let idx = _sort_indices cmp a.arr a.i a.j in
let cmp_int : int -> int -> int = Pervasives.compare in
let sort_indices cmp a = _sort_indices cmp a 0 (Array.length a) in
sort_indices cmp_int idx
(*$= & ~cmp:(=) ~printer:Q.Print.(array int)
[||] \
(let a = 1--6 in let s = make a 2 ~len:0 in \
sort_ranking Pervasives.compare s)
[|2;1;3;0|] \
(let a = [|"d";"c";"b";"e";"a"|] in let s = make a 1 ~len:4 in \
sort_ranking Pervasives.compare s)
*)
(*$Q
Q.(array_of_size Gen.(0--50) printable_string) (fun a -> \
Array.length a > 10 ==> ( Array.length a > 10 && \
let s = make a 5 ~len:5 in \
let b = sort_indices String.compare s in \
sorted String.compare s = Array.map (get s) b))
*)
let sort_indices cmp a = _sort_indices cmp a.arr a.i a.j
(*$= & ~cmp:(=) ~printer:Q.Print.(array int)
[||] \
(let a = 1--6 in let s = make a 2 ~len:0 in \
sort_indices Pervasives.compare s)
[|3;1;0;2|] \
(let a = [|"d";"c";"b";"e";"a"|] in let s = make a 1 ~len:4 in \
sort_indices Pervasives.compare s)
*)
(*$Q
Q.(array_of_size Gen.(0--60) printable_string) (fun a -> \
Array.length a > 10 ==> ( Array.length a > 10 && \
let s = make a 5 ~len:5 in \
let b = sort_ranking String.compare s in \
let a_sorted = sorted String.compare s in \
copy s = Array.map (Array.get a_sorted) b))
*)
let find f a = _find (fun _ -> f) a.arr a.i a.j
let findi f a = _find (fun i -> f (i-a.i)) a.arr a.i a.j
let find_idx p a =
_find (fun i x -> if p x then Some (i-a.i,x) else None) a.arr a.i a.j
(*$=
(Some (1,"c")) (find_idx ((=) "c") (make [| "a"; "b"; "c" |] 1 2))
*)
let lookup_exn ?(cmp=Pervasives.compare) k a =
_lookup_exn ~cmp k a.arr a.i (a.j-1) - a.i
let lookup ?(cmp=Pervasives.compare) k a =
try Some (_lookup_exn ~cmp k a.arr a.i (a.j-1) - a.i)
with Not_found -> None
(*$=
(Some 1) (lookup "c" (make [| "a"; "b"; "c" |] 1 2))
*)
let bsearch ?(cmp=Pervasives.compare) k a =
match bsearch_ ~cmp k a.arr a.i (a.j - 1) with
| `At m -> `At (m - a.i)
| `Just_after m -> `Just_after (m - a.i)
| res -> res
let for_all p a = _for_all p a.arr a.i a.j
let exists p a = _exists p a.arr a.i a.j
let for_all2 p a b =
length a = length b && _for_all2 p a.arr b.arr a.i b.i ~len:(length a)
let exists2 p a b =
_exists2 p a.arr b.arr a.i b.i ~len:(min (length a) (length b))
(*$T
exists2 (=) (make [| 1;2;3;4 |] 1 ~len:2) (make [| 0;1;3;4 |] 1 ~len:3)
*)
let _iter2 f a b i j ~len =
for o = 0 to len-1 do
f (Array.get a (i+o)) (Array.get b (j+o))
done
let iter2 f a b =
if length a <> length b then invalid_arg "iter2";
_iter2 f a.arr b.arr a.i b.i ~len:(length a)
let _fold2 f acc a b i j ~len =
let rec aux acc o =
if o=len then acc
else
let acc = f acc (Array.get a (i+o)) (Array.get b (j+o)) in
aux acc (o+1)
in
aux acc 0
let fold2 f acc a b =
if length a <> length b then invalid_arg "fold2";
_fold2 f acc a.arr b.arr a.i b.i ~len:(length a)
let shuffle a =
_shuffle Random.int a.arr a.i a.j
let shuffle_with st a =
_shuffle (Random.State.int st) a.arr a.i a.j
let random_choose a st = _choose a.arr a.i a.j st
let pp ?(sep=", ") pp_item buf a = _pp ~sep pp_item buf a.arr a.i a.j
let pp_i ?(sep=", ") pp_item out a =
_pp_i ~sep (fun k out x -> pp_item (k-a.i) out x) out a.arr a.i a.j
let to_seq a k = iter k a
let to_gen a = _to_gen a.arr a.i a.j
let to_klist a = _to_klist a.arr a.i a.j

View file

@ -1,194 +0,0 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Array Slice} *)
type 'a sequence = ('a -> unit) -> unit
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
type 'a gen = unit -> 'a option
type 'a equal = 'a -> 'a -> bool
type 'a ord = 'a -> 'a -> int
type 'a random_gen = Random.State.t -> 'a
type 'a printer = Format.formatter -> 'a -> unit
type 'a t
(** Array slice, containing elements of type ['a] *)
val empty : 'a t
val equal : 'a equal -> 'a t equal
val compare : 'a ord -> 'a t ord
val get : 'a t -> int -> 'a
val get_safe : 'a t -> int -> 'a option
(** [get_safe a i] returns [Some a.(i)] if [i] is a valid index
@since 0.18 *)
val make : 'a array -> int -> len:int -> 'a t
(** Create a slice from given offset and length..
@raise Invalid_argument if the slice isn't valid *)
val of_slice : ('a array * int * int) -> 'a t
(** Make a sub-array from a triple [(arr, i, len)] where [arr] is the array,
[i] the offset in [arr], and [len] the number of elements of the slice.
@raise Invalid_argument if the slice isn't valid (See {!make}) *)
val to_slice : 'a t -> ('a array * int * int)
(** Convert into a triple [(arr, i, len)] where [len] is the length of
the subarray of [arr] starting at offset [i] *)
val to_list : 'a t -> 'a list
(** Convert directly to a list
@since 1.0 *)
val full : 'a array -> 'a t
(** Slice that covers the full array *)
val underlying : 'a t -> 'a array
(** Underlying array (shared). Modifying this array will modify the slice *)
val copy : 'a t -> 'a array
(** Copy into a new array *)
val sub : 'a t -> int -> int -> 'a t
(** Sub-slice *)
val set : 'a t -> int -> 'a -> unit
val length : _ t -> int
val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a
(** Fold left on array, with index *)
val fold_while : ('a -> 'b -> 'a * [`Stop | `Continue]) -> 'a -> 'b t -> 'a
(** Fold left on array until a stop condition via [('a, `Stop)] is
indicated by the accumulator
@since 0.8 *)
val iter : ('a -> unit) -> 'a t -> unit
val iteri : (int -> 'a -> unit) -> 'a t -> unit
val blit : 'a t -> int -> 'a t -> int -> int -> unit
(** [blit from i into j len] copies [len] elements from the first array
to the second. See {!Array.blit}. *)
val reverse_in_place : 'a t -> unit
(** Reverse the array in place *)
val sorted : ('a -> 'a -> int) -> 'a t -> 'a array
(** [sorted cmp a] makes a copy of [a] and sorts it with [cmp].
@since 1.0 *)
val sort_indices : ('a -> 'a -> int) -> 'a t -> int array
(** [sort_indices cmp a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index at which the [i]-th element of [sorted cmp a]
appears in [a]. [a] is not modified.
In other words, [map (fun i -> a.(i)) (sort_indices cmp a) = sorted cmp a].
[sort_indices] yields the inverse permutation of {!sort_ranking}.
@since 1.0 *)
val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array
(** [sort_ranking cmp a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index at which the [i]-the element of [a] appears
in [sorted cmp a]. [a] is not modified.
In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a].
[sort_ranking] yields the inverse permutation of {!sort_indices}.
In the absence of duplicate elements in [a], we also have
[lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)]
@since 1.0 *)
val find : ('a -> 'b option) -> 'a t -> 'b option
(** [find f a] returns [Some y] if there is an element [x] such
that [f x = Some y], else it returns [None] *)
val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option
(** Like {!find}, but also pass the index to the predicate function.
@since 0.3.4 *)
val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option
(** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l],
and [p x] holds. Otherwise returns [None]
@since 0.3.4 *)
val lookup : ?cmp:'a ord -> 'a -> 'a t -> int option
(** Lookup the index of some value in a sorted array.
@return [None] if the key is not present, or
[Some i] ([i] the index of the key) otherwise *)
val lookup_exn : ?cmp:'a ord -> 'a -> 'a t -> int
(** Same as {!lookup}, but
@raise Not_found if the key is not present *)
val bsearch : ?cmp:('a -> 'a -> int) -> 'a -> 'a t ->
[ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ]
(** [bsearch ?cmp x arr] finds the index of the object [x] in the array [arr],
provided [arr] is {b sorted} using [cmp]. If the array is not sorted,
the result is not specified (may raise Invalid_argument).
Complexity: O(log n) where n is the length of the array
(dichotomic search).
@return
- [`At i] if [cmp arr.(i) x = 0] (for some i)
- [`All_lower] if all elements of [arr] are lower than [x]
- [`All_bigger] if all elements of [arr] are bigger than [x]
- [`Just_after i] if [arr.(i) < x < arr.(i+1)]
- [`Empty] if the array is empty
@raise Invalid_argument if the array is found to be unsorted w.r.t [cmp]
@since 0.13 *)
val for_all : ('a -> bool) -> 'a t -> bool
val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** Forall on pairs of arrays.
@raise Invalid_argument if they have distinct lengths
allow different types @since 0.20 *)
val exists : ('a -> bool) -> 'a t -> bool
val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** Exists on pairs of arrays.
@raise Invalid_argument if they have distinct lengths
allow different types @since 0.20 *)
val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc
(** Fold on two arrays stepwise.
@raise Invalid_argument if they have distinct lengths
@since 0.20 *)
val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit
(** Iterate on two arrays stepwise.
@raise Invalid_argument if they have distinct lengths
@since 0.20 *)
val shuffle : 'a t -> unit
(** Shuffle randomly the array, in place *)
val shuffle_with : Random.State.t -> 'a t -> unit
(** Like shuffle but using a specialized random state *)
val random_choose : 'a t -> 'a random_gen
(** Choose an element randomly.
@raise Not_found if the array/slice is empty *)
val to_seq : 'a t -> 'a sequence
val to_gen : 'a t -> 'a gen
val to_klist : 'a t -> 'a klist
(** {2 IO} *)
val pp: ?sep:string -> 'a printer -> 'a t printer
(** Print an array of items with printing function *)
val pp_i: ?sep:string -> (int -> 'a printer) -> 'a t printer
(** Print an array, giving the printing function both index and item *)

69
src/core/CCAtomic.ml Normal file
View file

@ -0,0 +1,69 @@
[@@@ifge 4.12]
include Atomic
[@@@else_]
open Stdlib (* for == *)
type 'a t = { mutable x: 'a }
let[@inline] make x = { x }
let[@inline] get { x } = x
let[@inline] set r x = r.x <- x
let[@inline never] exchange r x =
(* atomic *)
let y = r.x in
r.x <- x;
(* atomic *)
y
let[@inline never] compare_and_set r seen v =
(* atomic *)
if r.x == seen then (
r.x <- v;
(* atomic *)
true
) else
false
let[@inline never] fetch_and_add r x =
(* atomic *)
let v = r.x in
r.x <- x + r.x;
(* atomic *)
v
let[@inline never] incr r =
(* atomic *)
r.x <- 1 + r.x
(* atomic *)
let[@inline never] decr r =
(* atomic *)
r.x <- r.x - 1
(* atomic *)
[@@@endif]
(** Update loop with a compare-and-swap, and some basic backoff behavior.
[update_cas atomic f] is, in essence,
[let res, x = f !atomic in atomic := x; res]
done atomically. [f] might be called multiple times and must be as cheap
as possible.
@since 3.17 *)
let update_cas (type res) (self : 'a t) (f : 'a -> res * 'a) : res =
let exception Ret of res in
let backoff = ref 1 in
try
while true do
let old_val = get self in
let res, new_val = f old_val in
if compare_and_set self old_val new_val then raise_notrace (Ret res);
Containers_domain.relax_loop !backoff;
backoff := min 128 (2 * !backoff)
done;
assert false
with Ret r -> r

View file

@ -1,13 +1,29 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
type t = bool type t = bool
let equal (a:bool) b = a=b let equal (a : bool) b = Stdlib.( = ) a b
let compare (a : bool) b = Stdlib.compare a b
let compare (a:bool) b = Pervasives.compare a b let if_then f x =
if x then
Some (f ())
else
None
let negate = not let if_then_else f g x =
if x then
f ()
else
g ()
let to_int (x : bool) : int =
if x then
1
else
0
let of_int x : t = x <> 0
type 'a printer = Format.formatter -> 'a -> unit type 'a printer = Format.formatter -> 'a -> unit

View file

@ -1,18 +1,30 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Basic Bool functions} *) (** Basic Bool functions *)
type t = bool type t = bool
val compare : t -> t -> int val compare : t -> t -> int
(** Total ordering on booleans, similar to {!Pervasives.compare} *) (** [compare b1 b2] is the total ordering on booleans [b1] and [b2], similar to {!Stdlib.compare}. *)
val equal : t -> t -> bool val equal : t -> t -> bool
(** [equal b1 b2] is [true] if [b1] and [b2] are the same. *)
val negate : t -> t val if_then : (unit -> 'a) -> t -> 'a option
(** Negation on booleans (functional version of [not]) (** [if_then f x] is [Some (f ())] if [x] is true and None otherwise.
@deprecated since 1.3, simply use {!not} instead *) @since 3.13 *)
val if_then_else : (unit -> 'a) -> (unit -> 'a) -> t -> 'a
(** [if_then_else f g x] is [f ()] if [x] is true and [g ()] otherwise.
@since 3.13 *)
val to_int : t -> int
(** [to_int true = 1], [to_int false = 0].
@since 2.7 *)
val of_int : int -> t
(** [of_int i] is the same as [i <> 0]
@since 2.7 *)
type 'a printer = Format.formatter -> 'a -> unit type 'a printer = Format.formatter -> 'a -> unit

139
src/core/CCByte_buffer.ml Normal file
View file

@ -0,0 +1,139 @@
type 'a iter = ('a -> unit) -> unit
type t = {
mutable bs: bytes;
mutable len: int;
}
let create ?(cap = 0) () : t =
let bs =
if cap = 0 then
Bytes.unsafe_of_string ""
else
Bytes.create cap
in
{ len = 0; bs }
let[@inline] capacity self : int = Bytes.length self.bs
let[@inline] bytes self = self.bs
let[@inline] length self = self.len
let[@inline] is_empty self = self.len = 0
let[@inline] clear self = self.len <- 0
let grow_cap_ self =
min Sys.max_string_length
(let n = capacity self in
n + (n lsl 1) + 5)
let grow_to_ self newcap =
if newcap = capacity self then invalid_arg "byte_buf: cannot grow further";
let newbytes = Bytes.create newcap in
Bytes.blit self.bs 0 newbytes 0 self.len;
self.bs <- newbytes
let[@inline never] grow_ self =
let newcap = grow_cap_ self in
grow_to_ self newcap
let[@inline never] ensure_cap_grow_ self n =
(* new capacity, make sure it's at least [grow_cap_] so
that repeated calls to [ensure_cap] have the amortized complexity *)
let newcap = max n (grow_cap_ self) in
grow_to_ self newcap
let[@inline] ensure_cap self n =
if n > capacity self then ensure_cap_grow_ self n
let[@inline] ensure_free self n =
if n > capacity self - self.len then ensure_cap_grow_ self (self.len + n)
let[@inline] shrink_to self n = if self.len > n then self.len <- n
let append_buf (self : t) buf : unit =
let n = Buffer.length buf in
ensure_cap self (length self + n);
Buffer.blit buf 0 self.bs self.len n;
self.len <- self.len + n
let append_subbytes self b off len =
ensure_cap self (length self + len);
Bytes.blit b off self.bs self.len len;
self.len <- self.len + len
let[@inline] append_bytes self b = append_subbytes self b 0 (Bytes.length b)
let[@inline] append_string self s = append_bytes self (Bytes.unsafe_of_string s)
let[@inline] append_substring self s off len =
append_subbytes self (Bytes.unsafe_of_string s) off len
let[@inline] add_char_unsafe_ self c =
Bytes.unsafe_set self.bs self.len c;
self.len <- self.len + 1
let[@inline] add_char self c =
if self.len = capacity self then grow_ self;
add_char_unsafe_ self c
let[@inline] unsafe_get self i = Bytes.unsafe_get self.bs i
let[@inline] unsafe_set self i c = Bytes.unsafe_set self.bs i c
let[@inline] get self i =
if i < 0 || i >= self.len then invalid_arg "Byte_buf.get";
unsafe_get self i
let[@inline] set self i c =
if i < 0 || i >= self.len then invalid_arg "Byte_buf.set";
unsafe_set self i c
let[@inline] contents self = Bytes.sub_string self.bs 0 self.len
let[@inline] contents_bytes self = Bytes.sub self.bs 0 self.len
let[@inline] append_iter self i = i (add_char self)
let[@inline] append_seq self seq = Seq.iter (add_char self) seq
let[@inline] to_slice self = CCByte_slice.create ~len:self.len self.bs
let fold_left f acc self =
let { bs; len } = self in
(* capture current content *)
let acc = ref acc in
for i = 0 to len do
acc := f !acc (Bytes.unsafe_get bs i)
done;
!acc
let[@inline] iter f self =
(* capture current content *)
let { bs; len } = self in
for i = 0 to len do
f (Bytes.unsafe_get bs i)
done
let[@inline] iteri f self =
let { bs; len } = self in
for i = 0 to len do
f i (Bytes.unsafe_get bs i)
done
let of_seq seq =
let self = create ~cap:32 () in
append_seq self seq;
self
let of_iter iter =
let self = create ~cap:32 () in
append_iter self iter;
self
let to_iter self yield = iter yield self
let to_seq self =
let { bs; len } = self in
let rec s i () =
if i = len then
Seq.Nil
else
Seq.Cons (Bytes.unsafe_get bs i, s (i + 1))
in
s 0
(* TODO: unicode operators.*)

111
src/core/CCByte_buffer.mli Normal file
View file

@ -0,0 +1,111 @@
(** Byte buffer.
A dynamic vector of bytes that doesn't hide its internal from you.
Same use case as [Buffer.t] but with more power.
@since 3.7
*)
type t = {
mutable bs: bytes; (** The backing bytes buffer *)
mutable len: int;
(** Length of the "active" slice in [bs]. The actual content
of the buffer is [bs[0]..bs[len-1]]. What comes after
is undefined garbage. *)
}
(** The byte buffer.
The definition is public since 3.13.1 . *)
type 'a iter = ('a -> unit) -> unit
val create : ?cap:int -> unit -> t
(** Create a new buffer with given initial capacity. *)
val length : t -> int
(** Current length. *)
val is_empty : t -> bool
(** [is_empty b] is [length b=0] *)
val capacity : t -> int
(** Current capacity (size of the array returned by {!bytes}) *)
val bytes : t -> bytes
(** Access the underlying byte buffer. This buffer can change after
operations that affect the capacity (e.g. {!add_char}). *)
val clear : t -> unit
(** [clear buf] sets [buf.len <- 0]. This doesn't resize the byte buffer. *)
val ensure_cap : t -> int -> unit
(** [ensure_cap self n] ensures that [capacity self >= n].
@raise Invalid_argument if this requires the buffer to grow beyond system limits. *)
val ensure_free : t -> int -> unit
(** [ensure_free buf n] ensures that the free space at the end of the
buffer is at least [n].
@raise Invalid_argument if this requires the buffer to grow beyond system limits. *)
val shrink_to : t -> int -> unit
(** [shrink_to buf n] reduces [length buf] to at most [n].
Does nothing if the length is already <= n. *)
val add_char : t -> char -> unit
(** Push a character at the end.
@raise Invalid_argument if this requires the buffer to grow beyond system limits. *)
val append_bytes : t -> bytes -> unit
(** Add bytes at the end *)
val append_subbytes : t -> bytes -> int -> int -> unit
(** Add byte slice at the end *)
val append_string : t -> string -> unit
(** Add string at the end *)
val append_substring : t -> string -> int -> int -> unit
(** Add substring at the end *)
val append_buf : t -> Buffer.t -> unit
(** Add content of the buffer at the end *)
val append_iter : t -> char iter -> unit
(** Adds characters from the iter *)
val append_seq : t -> char Seq.t -> unit
(** Adds characters from the seq *)
val get : t -> int -> char
(** Get the char at the given offset *)
val unsafe_get : t -> int -> char
(** Get the char at the given offset, unsafe (no bound check) *)
val set : t -> int -> char -> unit
(** Set the char at the given offset *)
val unsafe_set : t -> int -> char -> unit
(** Set the char at the given offset, unsafe (no bound check) *)
val to_slice : t -> CCByte_slice.t
(** [to_slice buf] returns a slice of the current content.
The slice shares the same byte array as [buf] (until [buf] is resized).
@since 3.13.1 *)
val contents : t -> string
(** Copy the internal data to a string. Allocates. *)
val contents_bytes : t -> bytes
(** Copy the internal data to a {!bytes}. Allocates. *)
val iter : (char -> unit) -> t -> unit
(** Iterate on the content *)
val iteri : (int -> char -> unit) -> t -> unit
(** Iterate with index.
@since 3.13.1 *)
val fold_left : ('a -> char -> 'a) -> 'a -> t -> 'a
val of_iter : char iter -> t
val of_seq : char Seq.t -> t
val to_iter : t -> char iter
val to_seq : t -> char Seq.t

42
src/core/CCByte_slice.ml Normal file
View file

@ -0,0 +1,42 @@
type t = {
bs: bytes;
mutable off: int;
mutable len: int;
}
let show self = Printf.sprintf "<slice len=%d>" self.len
let pp out self = Format.pp_print_string out (show self)
let create ?(off = 0) ?len bs =
let len =
match len with
| None -> Bytes.length bs - off
| Some n ->
if n < 0 || off + n > Bytes.length bs then
invalid_arg "Bslice: invalid length";
n
in
{ bs; off; len }
let[@inline] unsafe_of_string ?off ?len s =
create ?off ?len (Bytes.unsafe_of_string s)
let[@inline] len self = self.len
let[@inline] contents self = Bytes.sub_string self.bs self.off self.len
let[@inline] get self i : char =
if i >= self.len then invalid_arg "Bslice: out of bound access";
Bytes.unsafe_get self.bs (self.off + i)
let[@inline] set self i c : unit =
if i >= self.len then invalid_arg "Bslice: out of bound access";
Bytes.unsafe_set self.bs (self.off + i) c
let sub self off len =
if off + len > self.len then invalid_arg "Bslice: invalid length";
{ bs = self.bs; off = self.off + off; len }
let[@inline] consume self n : unit =
if n > self.len then invalid_arg "Bslice: consuming too many bytes";
self.off <- self.off + n;
self.len <- self.len - n

49
src/core/CCByte_slice.mli Normal file
View file

@ -0,0 +1,49 @@
(** A simple byte slice.
@since 3.13.1 *)
type t = {
bs: bytes; (** The bytes, potentially shared between many slices *)
mutable off: int; (** Offset in [bs] *)
mutable len: int;
(** Length of the slice. Valid indices are [bs[off]…bs[off+len-1]],
inclusive. *)
}
val show : t -> string
(** Simple printer (summary, doesn't show the content) *)
val pp : Format.formatter -> t -> unit
(** Simple printer (summary, doesn't show the content) *)
val create : ?off:int -> ?len:int -> bytes -> t
(** [create bs] creates a slice of [bs].
@param off optional starting offset
@param len length of the slice *)
val unsafe_of_string : ?off:int -> ?len:int -> string -> t
(** [unsafe_of_string s] makes a slice from a string.
This is unsafe because mutating the bytes is forbidden
(just like with {!Bytes.unsafe_of_string} *)
val len : t -> int
(** Access the length *)
val get : t -> int -> char
(** [get sl i] gets the [i]-th byte of the slice. Same as [sl.bs.[sl.off + i]].
@raise Invalid_argument if out of bounds. *)
val set : t -> int -> char -> unit
(** [set sl i c] sets the [i]-th byte to [c].
@raise Invalid_argument if out of bounds. *)
val consume : t -> int -> unit
(** [consume sl n] moves the offset forward by [n] bytes, and
reduces [len] by [n] bytes. *)
val contents : t -> string
(** A copy of the contents of the slice. Allocates. *)
val sub : t -> int -> int -> t
(** [sub sl off len] makes a new slice with the same
backing [bs]. *)

View file

@ -0,0 +1,297 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Simple S-expression parsing/printing} *)
type 'a or_error = ('a, string) result
type 'a gen = unit -> 'a option
module type SEXP = CCSexp_intf.BASIC_SEXP
module type S = CCSexp_intf.S0
let equal_string (a : string) b = Stdlib.( = ) a b
let compare_string (a : string) b = Stdlib.compare a b
let _with_in filename f =
let ic = open_in filename in
try
let x = f ic in
close_in ic;
x
with e ->
close_in ic;
Error (Printexc.to_string e)
let _with_out filename f =
let oc = open_out filename in
try
let x = f oc in
close_out oc;
x
with e ->
close_out oc;
raise e
module Make (Sexp : SEXP) = struct
type t = Sexp.t
type sexp = t
let atom = Sexp.atom
let list = Sexp.list
let of_int x = Sexp.atom (string_of_int x)
let of_float x = Sexp.atom (string_of_float x)
let of_bool x = Sexp.atom (string_of_bool x)
let of_unit = Sexp.list []
let of_list l = Sexp.list l
let of_rev_list l = Sexp.list (List.rev l)
let of_pair (x, y) = Sexp.list [ x; y ]
let of_triple (x, y, z) = Sexp.list [ x; y; z ]
let of_quad (x, y, z, u) = Sexp.list [ x; y; z; u ]
let of_variant name args = Sexp.list (Sexp.atom name :: args)
let of_field name t = Sexp.list [ Sexp.atom name; t ]
let of_record l = Sexp.list (List.map (fun (n, x) -> of_field n x) l)
(** {3 Printing} *)
let rec to_buf b t =
Sexp.match_ t
~atom:(fun s -> Printf.bprintf b "%d:%s" (String.length s) s)
~list:(function
| [] -> Buffer.add_string b "()"
| [ x ] -> Printf.bprintf b "(%a)" to_buf x
| l ->
Buffer.add_char b '(';
List.iter (to_buf b) l;
Buffer.add_char b ')')
let to_string t =
let b = Buffer.create 128 in
to_buf b t;
Buffer.contents b
let rec pp_noindent fmt t =
Sexp.match_ t
~atom:(fun s -> Format.fprintf fmt "%d:%s" (String.length s) s)
~list:(function
| [] -> Format.pp_print_string fmt "()"
| [ x ] -> Format.fprintf fmt "(%a)" pp_noindent x
| l ->
Format.fprintf fmt "(";
List.iter (pp_noindent fmt) l;
Format.fprintf fmt ")")
let pp = pp_noindent
let rec to_chan oc t =
Sexp.match_ t
~atom:(fun s -> Printf.fprintf oc "%d:%s" (String.length s) s)
~list:(function
| [] -> output_string oc "()"
| [ x ] -> Printf.fprintf oc "(%a)" to_chan x
| l ->
output_char oc '(';
List.iter (to_chan oc) l;
output_char oc ')')
let to_file_iter filename iter =
_with_out filename (fun oc -> iter (fun t -> to_chan oc t))
let to_file filename t = to_file_iter filename (fun k -> k t)
(** {3 Parsing} *)
module type INPUT = sig
exception EOF
val read_char : unit -> char
val read_string : int -> string
end
module Decoder (I : INPUT) = struct
let[@inline] is_num_ c =
Char.code c >= Char.code '0' && Char.code c <= Char.code '9'
let[@inline] as_num_ c = Char.code c - Char.code '0'
let next_ () : sexp or_error * bool =
let rec read_string_len n =
match I.read_char () with
| c when is_num_ c -> read_string_len ((n * 10) + as_num_ c)
| ':' ->
let s = I.read_string n in
atom s
| _ -> failwith "expected string length"
and eat_colon () =
match I.read_char () with
| ':' -> ()
| _ -> failwith "expected ':'"
and read_in_paren acc =
match I.read_char () with
| ')' -> list (List.rev acc)
| c when is_num_ c ->
let sexp = read_string_len (as_num_ c) in
read_in_paren (sexp :: acc)
| '(' ->
let sexp = read_in_paren [] in
read_in_paren (sexp :: acc)
| _ -> failwith "expected list of sexprs"
in
(* read a S-expr *)
try
match I.read_char () with
| exception I.EOF -> Error "unexpected EOF", true
| '(' -> Ok (read_in_paren []), false
| '0' ->
eat_colon ();
Ok (atom ""), false
| c when is_num_ c -> Ok (read_string_len (as_num_ c)), false
| _ -> Error "unexpected char, expected toplevel sexpr", false
with Failure e -> Error e, false
let to_list () : _ or_error =
let rec iter acc =
match next_ () with
| Error _, true -> Ok (List.rev acc)
| Ok x, _ -> iter (x :: acc)
| (Error _ as res), _ -> res
in
try iter [] with e -> Error (Printexc.to_string e)
let[@inline] next_or_error () : _ or_error = fst (next_ ())
end
[@@inline]
module Decoder_str (X : sig
val s : string
end) =
Decoder (struct
exception EOF
let i = ref 0
let n = String.length X.s
let read_char () =
if !i >= n then raise_notrace EOF;
let c = String.unsafe_get X.s !i in
incr i;
c
let read_string len =
if !i + len > n then raise_notrace EOF;
let res = String.sub X.s !i len in
i := !i + len;
res
end)
[@@inline]
let parse_string s : t or_error =
let module D = Decoder_str (struct
let s = s
end) in
D.next_or_error ()
let parse_string_list s : t list or_error =
let module D = Decoder_str (struct
let s = s
end) in
D.to_list ()
module Decoder_ic (X : sig
val ic : in_channel
end) =
Decoder (struct
exception EOF = End_of_file
let[@inline] read_char () = input_char X.ic
let read_string n =
match n with
| 0 -> ""
| 1 -> String.make 1 (read_char ())
| _ ->
let buf = Bytes.make n '\000' in
let i = ref 0 in
while !i < n do
let len = input X.ic buf !i (n - !i) in
i := !i + len
done;
Bytes.unsafe_to_string buf
end)
[@@inline]
let parse_chan_ ?file ic : sexp or_error =
let module D = Decoder_ic (struct
let ic = ic
end) in
match D.next_or_error (), file with
| Error s, Some file -> Error (Printf.sprintf "%s in '%s'" s file)
| r, _ -> r
let parse_chan_list_ ?file ic =
let module D = Decoder_ic (struct
let ic = ic
end) in
match D.to_list (), file with
| Error s, Some file -> Error (Printf.sprintf "%s in '%s'" s file)
| r, _ -> r
let parse_chan ic = parse_chan_ ic
let parse_chan_list ic = parse_chan_list_ ic
let parse_chan_gen ic =
let module D = Decoder_ic (struct
let ic = ic
end) in
fun () ->
match D.next_ () with
| _, true -> None
| Error e, _ -> Some (Error e)
| Ok x, _ -> Some (Ok x)
let parse_file filename = _with_in filename (parse_chan_ ~file:filename)
let parse_file_list filename =
_with_in filename (parse_chan_list_ ~file:filename)
end
type t =
[ `Atom of string
| `List of t list
]
let rec equal a b =
match a, b with
| `Atom s1, `Atom s2 -> equal_string s1 s2
| `List l1, `List l2 ->
(try List.for_all2 equal l1 l2 with Invalid_argument _ -> false)
| `Atom _, _ | `List _, _ -> false
let rec compare_list a b =
match a, b with
| [], [] -> 0
| [], _ :: _ -> -1
| _ :: _, [] -> 1
| x :: xs, y :: ys ->
(match compare x y with
| 0 -> compare_list xs ys
| c -> c)
and compare a b =
match a, b with
| `Atom s1, `Atom s2 -> compare_string s1 s2
| `List l1, `List l2 -> compare_list l1 l2
| `Atom _, _ -> -1
| `List _, _ -> 1
module Basic_ = struct
type nonrec t = t
let atom x = `Atom x
let list x = `List x
let match_ x ~atom ~list =
match x with
| `Atom x -> atom x
| `List l -> list l
end
include (Make (Basic_) : S with type t := t)

View file

@ -0,0 +1,33 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** Canonical S-expressions
See {{: https://en.wikipedia.org/wiki/Canonical_S-expressions} wikipedia}.
These S-expressions are binary safe.
@since 3.3
*)
type 'a or_error = ('a, string) result
type 'a gen = unit -> 'a option
module type SEXP = CCSexp_intf.BASIC_SEXP
module type S = CCSexp_intf.S0
(** {2 Parser and printer} *)
module Make (Sexp : SEXP) : S with type t = Sexp.t
(** {2 Basics} *)
type t =
[ `Atom of string
| `List of t list
]
(** A simple, structural representation of S-expressions.
Compatible with {!CCSexp}. *)
include S with type t := t
val equal : t -> t -> bool
val compare : t -> t -> int
val atom : string -> t

View file

@ -6,21 +6,29 @@
include Char include Char
let equal (a:char) b = a=b let pp_buf = Buffer.add_char
let pp = Format.pp_print_char
let pp = Buffer.add_char
let print = Format.pp_print_char
let of_int_exn = Char.chr let of_int_exn = Char.chr
let of_int c = try Some (of_int_exn c) with _ -> None let of_int c = try Some (of_int_exn c) with Invalid_argument _ -> None
let to_int = Char.code let to_int = Char.code
let to_string c = String.make 1 c
let lowercase_ascii c = module Infix = struct
if c >= 'A' && c <= 'Z' let ( = ) : t -> t -> bool = Stdlib.( = )
then Char.unsafe_chr (Char. code c + 32) let ( <> ) : t -> t -> bool = Stdlib.( <> )
else c let ( < ) : t -> t -> bool = Stdlib.( < )
let ( > ) : t -> t -> bool = Stdlib.( > )
let ( <= ) : t -> t -> bool = Stdlib.( <= )
let ( >= ) : t -> t -> bool = Stdlib.( >= )
end
let uppercase_ascii c = include Infix
if c >= 'a' && c <= 'z'
then Char.unsafe_chr (Char.code c - 32) let is_uppercase_ascii c = c > '\064' && c < '\091'
else c let is_lowercase_ascii c = c > '\096' && c < '\123'
let is_letter_ascii c =
(is_lowercase_ascii [@inlined]) c || (is_uppercase_ascii [@inlined]) c
let is_digit_ascii c = c > '\047' && c < '\058'
let is_whitespace_ascii c = c = '\032' || (c > '\008' && c < '\014')

View file

@ -1,34 +1,93 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Utils around char} (** Utils around char
@since 0.14 *) @since 0.14 *)
include module type of Char (** @inline *)
include module type of struct
include Char
end
val equal : t -> t -> bool
val compare : t -> t -> int val compare : t -> t -> int
(** The comparison function for characters, with the same specification as
val lowercase_ascii : t -> t {!Stdlib.compare}. Along with the type [t], this function [compare]
(** See {!Char} allows the module [Char] to be passed as argument to the functors
@since 0.20 *) {!Set.Make} and {!Map.Make}. *)
val uppercase_ascii : t -> t
(** See {!Char}
@since 0.20 *)
val of_int_exn : int -> t val of_int_exn : int -> t
(** Alias to {!Char.chr} (** Alias to {!Char.chr}.
@raise Invalid_argument if the int is not within [0,...,255] Return the character with the given ASCII code.
@raise Invalid_argument if the int is not within [0 255].
@since 1.0 *) @since 1.0 *)
val of_int : int -> t option val of_int : int -> t option
(** Safe version of {!of_int} (** Safe version of {!of_int_exn}.
@since 1.0 *) @since 1.0 *)
val to_int : t -> int val to_int : t -> int
(** Alias to {!Char.code} (** Alias to {!Char.code}.
Return the ASCII code of the argument.
@since 1.0 *) @since 1.0 *)
val pp : Buffer.t -> t -> unit val to_string : t -> string
val print : Format.formatter -> t -> unit (** [to_string c] returns a string containing [c]
@since 2.7 *)
val pp_buf : Buffer.t -> t -> unit
(** Renamed from [pp] since 2.0. *)
val pp : Format.formatter -> t -> unit
(** Renamed from [print] since 2.0. *)
val is_uppercase_ascii : t -> bool
(** [is_uppercase_ascii c] is true exactly when [c] is an
uppercase ASCII character, i.e. ['\064'] < [c] < ['\091'].
@since 3.16 *)
val is_lowercase_ascii : t -> bool
(** [is_lowercase_ascii c] is true exactly when [c] is a
lowercase ASCII character, i.e. ['\096'] < [c] < ['\123'].
@since 3.16 *)
val is_letter_ascii : t -> bool
(** [is_letter_ascii c] is true exactly when [c] is an ASCII
letter, i.e. [is_uppercase_ascii c || is_lowercase_ascii c].
@since 3.16 *)
val is_digit_ascii : t -> bool
(** [is_digit_ascii c] is true exactly when [c] is an
ASCII digit, i.e. ['\047'] < [c] < ['\058'].
@since 3.16 *)
val is_whitespace_ascii : t -> bool
(** [is_whitespace_ascii c] is true exactly when [c] is an ASCII
whitespace character as defined by Unicode, i.e. either [c = ' ']
or ['\008'] < [c] < ['\014'].
@since 3.16 *)
(** {2 Infix Operators}
@since 3.3 *)
module Infix : sig
val ( = ) : t -> t -> bool
(** @since 3.3 *)
val ( <> ) : t -> t -> bool
(** @since 3.3 *)
val ( < ) : t -> t -> bool
(** @since 3.3 *)
val ( > ) : t -> t -> bool
(** @since 3.3 *)
val ( <= ) : t -> t -> bool
(** @since 3.3 *)
val ( >= ) : t -> t -> bool
(** @since 3.3 *)
end
include module type of Infix

69
src/core/CCEither.ml Normal file
View file

@ -0,0 +1,69 @@
(* This file is free software, part of containers. See file "license" for more details. *)
type 'a iter = ('a -> unit) -> unit
type 'a equal = 'a -> 'a -> bool
type 'a ord = 'a -> 'a -> int
type 'a printer = Format.formatter -> 'a -> unit
(** {2 Basics} *)
type ('a, 'b) t = ('a, 'b) Either.t =
| Left of 'a
| Right of 'b
let left l = Left l
let right r = Right r
let is_left = function
| Left _ -> true
| Right _ -> false
let is_right = function
| Left _ -> false
| Right _ -> true
let find_left = function
| Left l -> Some l
| Right _ -> None
let find_right = function
| Left _ -> None
| Right r -> Some r
let map_left f = function
| Left l -> Left (f l)
| Right r -> Right r
let map_right f = function
| Left l -> Left l
| Right r -> Right (f r)
let map ~left ~right = function
| Left l -> Left (left l)
| Right r -> Right (right r)
let fold ~left ~right = function
| Left l -> left l
| Right r -> right r
let iter = fold
let for_all = fold
let equal ~left ~right e1 e2 =
match e1, e2 with
| Left l1, Left l2 -> left l1 l2
| Right r1, Right r2 -> right r1 r2
| _ -> false
let compare ~left ~right e1 e2 =
match e1, e2 with
| Left _, Right _ -> -1
| Right _, Left _ -> 1
| Left l1, Left l2 -> left l1 l2
| Right r1, Right r2 -> right r1 r2
(** {2 IO} *)
let pp ~left ~right fmt = function
| Left l -> Format.fprintf fmt "Left@ (@[%a@])" left l
| Right r -> Format.fprintf fmt "Right@ (@[%a@])" right r

76
src/core/CCEither.mli Normal file
View file

@ -0,0 +1,76 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** Either Monad
Module that is compatible with Either from OCaml 4.12 but can be use with any
ocaml version compatible with container
@since 3.2
*)
type 'a iter = ('a -> unit) -> unit
type 'a equal = 'a -> 'a -> bool
type 'a ord = 'a -> 'a -> int
type 'a printer = Format.formatter -> 'a -> unit
(** {2 Basics} *)
type ('a, 'b) t = ('a, 'b) Either.t =
| Left of 'a
| Right of 'b
val left : 'a -> ('a, 'b) t
(** [left l] is [Left l] *)
val right : 'b -> ('a, 'b) t
(** [right r] is [Right r] *)
val is_left : ('a, 'b) t -> bool
(** [is_left x] checks if [x = Left _] *)
val is_right : ('a, 'b) t -> bool
(** [is_right x] checks if [x = Right _] *)
val find_left : ('a, 'b) t -> 'a option
(** [find_left x] returns [l] if [x = Left l] and [None] otherwise. *)
val find_right : ('a, 'b) t -> 'b option
(** [find_right x] returns [r] if [x = Left r] and [None] otherwise. *)
val map_left : ('a1 -> 'a2) -> ('a1, 'b) t -> ('a2, 'b) t
(** Map of the Left variant. *)
val map_right : ('b1 -> 'b2) -> ('a, 'b1) t -> ('a, 'b2) t
(** Map of the Right variant. *)
val map :
left:('a1 -> 'a2) -> right:('b1 -> 'b2) -> ('a1, 'b1) t -> ('a2, 'b2) t
(** Map using [left] or [right]. *)
val fold : left:('a -> 'c) -> right:('b -> 'c) -> ('a, 'b) t -> 'c
(** Fold using [left] or [right]. *)
val iter : left:('a -> unit) -> right:('b -> unit) -> ('a, 'b) t -> unit
(** Iter using [left] or [right]. *)
val for_all : left:('a -> bool) -> right:('b -> bool) -> ('a, 'b) t -> bool
(** Check some property on [Left] or [Right] variant. *)
val equal :
left:('a -> 'a -> bool) ->
right:('b -> 'b -> bool) ->
('a, 'b) t ->
('a, 'b) t ->
bool
val compare :
left:('a -> 'a -> int) ->
right:('b -> 'b -> int) ->
('a, 'b) t ->
('a, 'b) t ->
int
(** {2 IO} *)
val pp : left:'a printer -> right:'b printer -> ('a, 'b) t printer
(** Pretty printer. *)

View file

@ -1,50 +1,46 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Equality Combinators} *) (** {1 Equality Combinators} *)
type 'a t = 'a -> 'a -> bool type 'a t = 'a -> 'a -> bool
let poly = (=) let poly = Stdlib.( = )
let physical = Stdlib.( == )
let int : int t = (=) let int : int t = ( = )
let string : string t = (=) let string : string t = Stdlib.( = )
let bool : bool t = (=) let bool : bool t = Stdlib.( = )
let float : float t = (=) let float : float t = Stdlib.( = )
let unit () () = true let unit () () = true
let rec list f l1 l2 = match l1, l2 with let rec list f l1 l2 =
match l1, l2 with
| [], [] -> true | [], [] -> true
| [], _ | _, [] -> false | [], _ | _, [] -> false
| x1::l1', x2::l2' -> f x1 x2 && list f l1' l2' | x1 :: l1', x2 :: l2' -> f x1 x2 && list f l1' l2'
let array eq a b = let array eq a b =
let rec aux i = let rec aux i =
if i = Array.length a then true if i = Array.length a then
else eq a.(i) b.(i) && aux (i+1) true
else
eq a.(i) b.(i) && aux (i + 1)
in in
Array.length a = Array.length b Array.length a = Array.length b && aux 0
&&
aux 0
let option f o1 o2 = match o1, o2 with let option f o1 o2 =
match o1, o2 with
| None, None -> true | None, None -> true
| Some _, None | Some _, None | None, Some _ -> false
| None, Some _ -> false
| Some x, Some y -> f x y | Some x, Some y -> f x y
let pair f g (x1,y1)(x2,y2) = f x1 x2 && g y1 y2 let pair f g (x1, y1) (x2, y2) = f x1 x2 && g y1 y2
let triple f g h (x1,y1,z1)(x2,y2,z2) = f x1 x2 && g y1 y2 && h z1 z2 let triple f g h (x1, y1, z1) (x2, y2, z2) = f x1 x2 && g y1 y2 && h z1 z2
let map f eq x y = eq (f x) (f y) let map f eq x y = eq (f x) (f y)
let always_eq _ _ = true
(*$Q let never_eq _ _ = false
Q.(let p = small_list (pair small_int bool) in pair p p) (fun (l1,l2) -> \
CCEqual.(list (pair int bool)) l1 l2 = (l1=l2))
*)
module Infix = struct module Infix = struct
let (>|=) x f = map f x let ( >|= ) x f = map f x
end end
include Infix include Infix

View file

@ -1,7 +1,8 @@
(* AUTOGENERATED FROM CCEqualLabels.mli *)
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Equality Combinators} *) (** Equality Combinators *)
(** @since 1.2 *) (** @since 1.2 *)
@ -9,17 +10,19 @@ type 'a t = 'a -> 'a -> bool
(** Equality function. Must be transitive, symmetric, and reflexive. *) (** Equality function. Must be transitive, symmetric, and reflexive. *)
val poly : 'a t val poly : 'a t
(** Standard polymorphic equality *) (** Standard polymorphic equality. *)
val physical : 'a t
(** Standard physical equality.
@since 2.0 *)
val int : int t val int : int t
val string : string t val string : string t
val bool : bool t val bool : bool t
val float : float t val float : float t
val unit : unit t val unit : unit t
val list : 'a t -> 'a list t val list : 'a t -> 'a list t
val array : 'a t -> 'a array t val array : 'a t -> 'a array t
val option : 'a t -> 'a option t val option : 'a t -> 'a option t
val pair : 'a t -> 'b t -> ('a * 'b) t val pair : 'a t -> 'b t -> ('a * 'b) t
val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
@ -32,9 +35,19 @@ val map : ('a -> 'b) -> 'b t -> 'a t
[map fst int] compares values of type [(int * 'a)] by their [map fst int] compares values of type [(int * 'a)] by their
first component. *) first component. *)
val (>|=) : 'b t -> ('a -> 'b) -> 'a t val always_eq : _ t
(** Infix equivalent of {!map} *) (** Always returns true. All values are equal.
@since 3.0 *)
val never_eq : _ t
(** Always returns false. No values are, so this
is not even reflexive (i.e. [x=x] is false).
Be careful!
@since 3.0 *)
module Infix : sig module Infix : sig
val (>|=) : 'b t -> ('a -> 'b) -> 'a t val ( >|= ) : 'b t -> ('a -> 'b) -> 'a t
(** Infix equivalent of {!map}. *)
end end
include module type of Infix

View file

@ -0,0 +1,3 @@
(* This file is free software, part of containers. See file "license" for more details. *)
include CCEqual

View file

@ -0,0 +1,51 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** Equality Combinators (Labeled version of {!CCEqual}) *)
(** @since 1.2 *)
type 'a t = 'a -> 'a -> bool
(** Equality function. Must be transitive, symmetric, and reflexive. *)
val poly : 'a t
(** Standard polymorphic equality. *)
val physical : 'a t
(** Standard physical equality.
@since 2.0 *)
val int : int t
val string : string t
val bool : bool t
val float : float t
val unit : unit t
val list : 'a t -> 'a list t
val array : 'a t -> 'a array t
val option : 'a t -> 'a option t
val pair : 'a t -> 'b t -> ('a * 'b) t
val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
val map : f:('a -> 'b) -> 'b t -> 'a t
(** [map f eq] is the equality function that, given objects [x] and [y],
projects [x] and [y] using [f] (e.g. using a record field) and then
compares those projections with [eq].
Example:
[map fst int] compares values of type [(int * 'a)] by their
first component. *)
val always_eq : _ t
(** Always returns true. All values are equal.
@since 3.9 *)
val never_eq : _ t
(** Always returns false. No values are, so this
is not even reflexive (i.e. [x=x] is false).
Be careful!
@since 3.9 *)
module Infix : sig
val ( >|= ) : 'b t -> ('a -> 'b) -> 'a t
(** Infix equivalent of {!map}. *)
end
include module type of Infix

View file

@ -1,46 +1,74 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
type t = float type t = float
type fpclass = Pervasives.fpclass =
type fpclass = Stdlib.fpclass =
| FP_normal | FP_normal
| FP_subnormal | FP_subnormal
| FP_zero | FP_zero
| FP_infinite | FP_infinite
| FP_nan | FP_nan
let nan = Pervasives.nan module Infix = struct
let ( = ) : t -> t -> bool = Stdlib.( = )
let ( <> ) : t -> t -> bool = Stdlib.( <> )
let ( < ) : t -> t -> bool = Stdlib.( < )
let ( > ) : t -> t -> bool = Stdlib.( > )
let ( <= ) : t -> t -> bool = Stdlib.( <= )
let ( >= ) : t -> t -> bool = Stdlib.( >= )
let ( ~- ) : t -> t = Stdlib.( ~-. )
let ( + ) : t -> t -> t = Stdlib.( +. )
let ( - ) : t -> t -> t = Stdlib.( -. )
let ( * ) : t -> t -> t = Stdlib.( *. )
let ( / ) : t -> t -> t = Stdlib.( /. )
end
let infinity = Pervasives.infinity include Infix
let neg_infinity = Pervasives.neg_infinity
[@@@ocaml.warning "-32"]
let nan = Stdlib.nan
let infinity = Stdlib.infinity
let neg_infinity = Stdlib.neg_infinity
let max_value = infinity let max_value = infinity
let min_value = neg_infinity let min_value = neg_infinity
let max_finite_value = Stdlib.max_float
let max_finite_value = Pervasives.max_float let epsilon = Stdlib.epsilon_float
let pi = 0x1.921fb54442d18p+1
let epsilon = Pervasives.epsilon_float let is_nan x = Stdlib.(classify_float x = Stdlib.FP_nan)
let add = ( +. )
let is_nan x = (x : t) <> x let sub = ( -. )
let mul = ( *. )
let add = (+.) let div = ( /. )
let sub = (-.) let neg = ( ~-. )
let neg = (~-.) let abs = Stdlib.abs_float
let abs = Pervasives.abs_float
let scale = ( *. ) let scale = ( *. )
let min (x : t) y = let min (x : t) y =
if is_nan x || is_nan y then nan match Stdlib.classify_float x, Stdlib.classify_float y with
else if x < y then x else y | FP_nan, _ -> y
| _, FP_nan -> x
| _ ->
if x < y then
x
else
y
let max (x : t) y = let max (x : t) y =
if is_nan x || is_nan y then nan match Stdlib.classify_float x, Stdlib.classify_float y with
else if x > y then x else y | FP_nan, _ -> y
| _, FP_nan -> x
| _ ->
if x > y then
x
else
y
let equal (a:float) b = a=b let equal (a : float) b = a = b
let hash : t -> int = Hashtbl.hash
let compare (a : float) b = Stdlib.compare a b
let hash = Hashtbl.hash [@@@ocaml.warning "+32"]
let compare (a:float) b = Pervasives.compare a b
type 'a printer = Format.formatter -> 'a -> unit type 'a printer = Format.formatter -> 'a -> unit
type 'a random_gen = Random.State.t -> 'a type 'a random_gen = Random.State.t -> 'a
@ -48,49 +76,39 @@ type 'a random_gen = Random.State.t -> 'a
let pp = Format.pp_print_float let pp = Format.pp_print_float
let fsign a = let fsign a =
if is_nan a then nan if is_nan a then
else if a = 0. then a nan
else Pervasives.copysign 1. a else if a = 0. then
a
else
Stdlib.copysign 1. a
exception TrapNaN of string exception TrapNaN of string
let sign_exn (a:float) = let sign_exn (a : float) =
if is_nan a then raise (TrapNaN "sign_exn") if is_nan a then
else compare a 0. raise (TrapNaN "sign_exn")
else
compare a 0.
let round x = let round x =
let low = floor x in let low = floor x in
let high = ceil x in let high = ceil x in
if x-.low > high-.x then high else low if x -. low > high -. x then
high
else
low
(*$= let to_int (a : float) = Stdlib.int_of_float a
2. (round 1.6) let of_int (a : int) = Stdlib.float_of_int a
1. (round 1.4) let to_string (a : float) = Stdlib.string_of_float a
0. (round 0.) let of_string_exn (a : string) = Stdlib.float_of_string a
*)
let to_int (a:float) = Pervasives.int_of_float a
let of_int (a:int) = Pervasives.float_of_int a
let to_string (a:float) = Pervasives.string_of_float a
let of_string_exn (a:string) = Pervasives.float_of_string a
let of_string (a:string) = Pervasives.float_of_string a
let of_string_opt (a : string) =
try Some (Stdlib.float_of_string a) with Failure _ -> None
let random n st = Random.State.float st n let random n st = Random.State.float st n
let random_small = random 100.0 let random_small = random 100.0
let random_range i j st = i +. random (j-.i) st let random_range i j st = i +. random (j -. i) st
let equal_precision ~epsilon a b = abs_float (a -. b) < epsilon
let equal_precision ~epsilon a b = abs_float (a-.b) < epsilon let classify = Stdlib.classify_float
let classify = Pervasives.classify_float
module Infix = struct
let (=) = Pervasives.(=)
let (<>) = Pervasives.(<>)
let (<) = Pervasives.(<)
let (>) = Pervasives.(>)
let (<=) = Pervasives.(<=)
let (>=) = Pervasives.(>=)
end
include Infix

View file

@ -1,11 +1,11 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Basic Float functions} (** Basic operations on floating-point numbers
@since 0.6.1 *) @since 0.6.1 *)
type t = float type t = float
type fpclass = Pervasives.fpclass =
type fpclass = Stdlib.fpclass =
| FP_normal | FP_normal
| FP_subnormal | FP_subnormal
| FP_zero | FP_zero
@ -13,41 +13,61 @@ type fpclass = Pervasives.fpclass =
| FP_nan | FP_nan
val nan : t val nan : t
(** [nan] is Not a Number (NaN). Equal to {!Stdlib.nan}. *)
val max_value : t val max_value : t
(** [max_value] is Positive infinity. Equal to {!Stdlib.infinity}. *)
val min_value : t val min_value : t
(** [min_value] is Negative infinity. Equal to {!Stdlib.neg_infinity}. *)
val max_finite_value : t val max_finite_value : t
(** [max_finite_value] is the largest finite float value. Equal to {!Stdlib.max_float}. *)
val epsilon : t val epsilon : t
(** [epsilon] is the smallest positive float x such that [1.0 +. x <> 1.0].
Equal to {!Stdlib.epsilon_float}. *)
val pi : t
(** [pi] is the constant pi. The ratio of a circumference to its diameter.
@since 3.0 *)
val is_nan : t -> bool val is_nan : t -> bool
(** [is_nan f] returns [true] if f is NaN, [false] otherwise. *)
val add : t -> t -> t val add : t -> t -> t
(** [add x y] is equal to [x +. y]. *)
val sub : t -> t -> t val sub : t -> t -> t
(** [sub x y] is equal to [x -. y]. *)
val neg : t -> t val neg : t -> t
(** [neg x] is equal to [~-. x]. *)
val abs : t -> t val abs : t -> t
(** [abs x] is the absolute value of the floating-point number [x].
Equal to {!Stdlib.abs_float}. *)
val scale : t -> t -> t val scale : t -> t -> t
(** [scale x y] is equal to [x *. y]. *)
val min : t -> t -> t val min : t -> t -> t
(** [min x y] returns the min of the two given values [x] and [y]. *)
val max : t -> t -> t val max : t -> t -> t
(** [max x y] returns the max of the two given values [x] and [y]. *)
val equal : t -> t -> bool val equal : t -> t -> bool
(** [equal x y] is [true] if [x] and [y] are the same. *)
val compare : t -> t -> int val compare : t -> t -> int
(** [compare x y] is {!Stdlib.compare x y}. *)
type 'a printer = Format.formatter -> 'a -> unit type 'a printer = Format.formatter -> 'a -> unit
type 'a random_gen = Random.State.t -> 'a type 'a random_gen = Random.State.t -> 'a
val pp : t printer val pp : t printer
val hash : t -> int val hash : t -> int
val random : t -> t random_gen val random : t -> t random_gen
val random_small : t random_gen val random_small : t random_gen
val random_range : t -> t -> t random_gen val random_range : t -> t -> t random_gen
@ -57,13 +77,15 @@ val fsign : t -> t
@since 0.7 *) @since 0.7 *)
val round : t -> t val round : t -> t
(** [round f] returns the closest integer value, either above or below (** [round x] returns the closest integer value, either above or below.
For [n + 0.5], [round] returns [n].
@since 0.20 *) @since 0.20 *)
exception TrapNaN of string exception TrapNaN of string
val sign_exn : t -> int val sign_exn : t -> int
(** [sign_exn x] will return the sign of [x] as [1, 0] or [-1], or raise an (** [sign_exn x] will return the sign of [x] as [1, 0] or [-1], or raise an
exception [TrapNaN] if [x] is a NaN. exception [TrapNaN] if [x] is NaN.
Note that infinities have defined signs in OCaml. Note that infinities have defined signs in OCaml.
@since 0.7 *) @since 0.7 *)
@ -72,46 +94,67 @@ val to_int : t -> int
Unspecified if outside of the range of integers. *) Unspecified if outside of the range of integers. *)
val of_int : int -> t val of_int : int -> t
(** Alias to {!float_of_int} *) (** Alias to {!float_of_int}. *)
val to_string : t -> string val to_string : t -> string
val of_string_exn : string -> t val of_string_exn : string -> t
(** Alias to {!float_of_string} (** Alias to {!float_of_string}.
@raise Failure in case of failure @raise Failure in case of failure.
@since 1.2 *) @since 1.2 *)
val of_string : string -> t val of_string_opt : string -> t option
(** Alias to {!float_of_string}. (** @since 3.0 *)
@deprecated since 1.2, use {!of_string_exn} instead
@raise Failure in case of failure *)
val equal_precision : epsilon:t -> t -> t -> bool val equal_precision : epsilon:t -> t -> t -> bool
(** Equality with allowed error up to a non negative epsilon value *) (** Equality with allowed error up to a non negative epsilon value. *)
val classify : t -> fpclass val classify : t -> fpclass
(** [classify x] returns the class of the given floating-point number [x]:
normal, subnormal, zero, infinite or nan (not a number). *)
(** {2 Infix Operators} (** {2 Infix Operators}
@since 0.17 *) @since 0.17 *)
module Infix : sig module Infix : sig
val (=) : t -> t -> bool val ( = ) : t -> t -> bool
(** @since 0.17 *) (** @since 0.17 *)
val (<>) : t -> t -> bool val ( <> ) : t -> t -> bool
(** @since 0.17 *) (** @since 0.17 *)
val (<) : t -> t -> bool val ( < ) : t -> t -> bool
(** @since 0.17 *) (** @since 0.17 *)
val (>) : t -> t -> bool val ( > ) : t -> t -> bool
(** @since 0.17 *) (** @since 0.17 *)
val (<=) : t -> t -> bool val ( <= ) : t -> t -> bool
(** @since 0.17 *) (** @since 0.17 *)
val (>=) : t -> t -> bool val ( >= ) : t -> t -> bool
(** @since 0.17 *) (** @since 0.17 *)
val ( + ) : t -> t -> t
(** Addition.
@since 2.1 *)
val ( - ) : t -> t -> t
(** Subtraction.
@since 2.1 *)
val ( ~- ) : t -> t
(** Unary negation.
@since 2.1 *)
val ( * ) : t -> t -> t
(** Multiplication.
@since 2.1 *)
val ( / ) : t -> t -> t
(** Division.
@since 2.1 *)
end end
include module type of Infix include module type of Infix

View file

@ -1,126 +1,113 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Helpers for Format} *) (** {1 Helpers for Format} *)
type 'a sequence = ('a -> unit) -> unit type 'a iter = ('a -> unit) -> unit
include Format
type t = Format.formatter type t = Format.formatter
type 'a printer = t -> 'a -> unit type -'a printer = t -> 'a -> unit
(** {2 Combinators} *) (** {2 Combinators} *)
let silent _fmt _ = () let silent _fmt _ = ()
let return fmt_str out () = Format.fprintf out "%(%)" fmt_str let return fmt_str out () = Format.fprintf out "%(%)" fmt_str
(*$inject
let to_string_test s = CCFormat.sprintf_no_color "@[<h>%a@]%!" s ()
*)
(*$= & ~printer:(fun s->CCFormat.sprintf "%S" s)
"a b" (to_string_test (return "a@ b"))
", " (to_string_test (return ",@ "))
"and then" (to_string_test (return "@{<Red>and then@}@,"))
"a b" (to_string_test (return "@[<h>a@ b@]"))
*)
let unit fmt () = Format.pp_print_string fmt "()" let unit fmt () = Format.pp_print_string fmt "()"
let int fmt i = Format.pp_print_string fmt (string_of_int i) let int fmt i = Format.pp_print_string fmt (string_of_int i)
let string = Format.pp_print_string let string = Format.pp_print_string
let bool = Format.pp_print_bool let bool = Format.pp_print_bool
let float3 fmt f = Format.fprintf fmt "%.3f" f let float3 fmt f = Format.fprintf fmt "%.3f" f
let float fmt f = Format.pp_print_string fmt (string_of_float f) let float fmt f = Format.pp_print_string fmt (string_of_float f)
let char = Format.pp_print_char let char = Format.pp_print_char
let int32 fmt n = Format.fprintf fmt "%ld" n let int32 fmt n = Format.fprintf fmt "%ld" n
let int64 fmt n = Format.fprintf fmt "%Ld" n let int64 fmt n = Format.fprintf fmt "%Ld" n
let nativeint fmt n = Format.fprintf fmt "%nd" n let nativeint fmt n = Format.fprintf fmt "%nd" n
let string_quoted fmt s = Format.fprintf fmt "\"%s\"" s let string_quoted fmt s = Format.fprintf fmt "\"%s\"" s
let flush = Format.pp_print_flush let flush = Format.pp_print_flush
let space = Format.pp_print_space
let cut = Format.pp_print_cut
let break fmt (m, n) = Format.pp_print_break fmt m n
let newline = Format.pp_force_newline let newline = Format.pp_force_newline
let substring out (s, i, len) : unit = string out (String.sub s i len)
let text = Format.pp_print_text
let substring out (s,i,len): unit = let string_lines out (s : string) : unit =
string out (String.sub s i len) fprintf out "@[<v>";
let text out (s:string): unit =
let len = String.length s in
let i = ref 0 in let i = ref 0 in
let search_ c = let n = String.length s in
try Some (String.index_from s !i c) with Not_found -> None while !i < n do
in let j = try String.index_from s !i '\n' with Not_found -> n in
while !i < len do if !i > 0 then fprintf out "@,";
let j_newline = search_ '\n' in substring out (s, !i, j - !i);
let j_space = search_ ' ' in i := j + 1
let on_newline j = done;
substring out (s, !i, j - !i); fprintf out "@]"
newline out ();
i := j + 1
and on_space j =
substring out (s, !i, j - !i);
Format.pp_print_space out ();
i := j + 1
in
begin match j_newline, j_space with
| None, None ->
(* done *)
substring out (s, !i, len - !i);
i := len
| Some j, None -> on_newline j
| None, Some j -> on_space j
| Some j1, Some j2 ->
if j1<j2 then on_newline j1 else on_space j2
end
done
(*$= & ~printer:(fun s->CCFormat.sprintf "%S" s) let list ?(sep = return ",@ ") pp fmt l =
"a\nb\nc" (sprintf_no_color "@[<v>%a@]%!" text "a b c") let rec pp_list l =
"a b\nc" (sprintf_no_color "@[<h>%a@]%!" text "a b\nc") match l with
*) | x :: (_ :: _ as l) ->
let list ?(sep=return ",@ ") pp fmt l =
let rec pp_list l = match l with
| x::((_::_) as l) ->
pp fmt x; pp fmt x;
sep fmt (); sep fmt ();
pp_list l pp_list l
| x::[] -> pp fmt x | [ x ] -> pp fmt x
| [] -> () | [] -> ()
in in
pp_list l pp_list l
let array ?(sep=return ",@ ") pp fmt a = let array ?(sep = return ",@ ") pp fmt a =
for i = 0 to Array.length a - 1 do for i = 0 to Array.length a - 1 do
if i > 0 then sep fmt (); if i > 0 then sep fmt ();
pp fmt a.(i) pp fmt a.(i)
done done
let arrayi ?(sep=return ",@ ") pp fmt a = let arrayi ?(sep = return ",@ ") pp fmt a =
for i = 0 to Array.length a - 1 do for i = 0 to Array.length a - 1 do
if i > 0 then sep fmt (); if i > 0 then sep fmt ();
pp fmt (i, a.(i)) pp fmt (i, a.(i))
done done
let seq ?(sep=return ",@ ") pp fmt seq = let seq ?(sep = return ",@ ") pp fmt seq =
let first = ref true in let first = ref true in
seq CCSeq.iter
(fun x -> (fun x ->
if !first then first := false else sep fmt (); if !first then
pp fmt x) first := false
else
sep fmt ();
pp fmt x)
seq
let opt pp fmt x = match x with let iter ?(sep = return ",@ ") pp fmt seq =
let first = ref true in
seq (fun x ->
if !first then
first := false
else
sep fmt ();
pp fmt x)
let opt pp fmt x =
match x with
| None -> Format.pp_print_string fmt "none" | None -> Format.pp_print_string fmt "none"
| Some x -> Format.fprintf fmt "some %a" pp x | Some x -> Format.fprintf fmt "some %a" pp x
let pair ?(sep=return ",@ ") ppa ppb fmt (a, b) = let pair ?(sep = return ",@ ") ppa ppb fmt (a, b) =
Format.fprintf fmt "%a%a%a" ppa a sep () ppb b Format.fprintf fmt "%a%a%a" ppa a sep () ppb b
let triple ?(sep=return ",@ ") ppa ppb ppc fmt (a, b, c) = let triple ?(sep = return ",@ ") ppa ppb ppc fmt (a, b, c) =
Format.fprintf fmt "%a%a%a%a%a" ppa a sep () ppb b sep () ppc c Format.fprintf fmt "%a%a%a%a%a" ppa a sep () ppb b sep () ppc c
let quad ?(sep=return ",@ ") ppa ppb ppc ppd fmt (a, b, c, d) = let quad ?(sep = return ",@ ") ppa ppb ppc ppd fmt (a, b, c, d) =
Format.fprintf fmt "%a%a%a%a%a%a%a" ppa a sep () ppb b sep () ppc c sep () ppd d Format.fprintf fmt "%a%a%a%a%a%a%a" ppa a sep () ppb b sep () ppc c sep () ppd
d
let append ppa ppb fmt () =
ppa fmt ();
ppb fmt ()
let append_l ppl fmt () = List.iter (fun pp -> pp fmt ()) ppl
let within a b p out x = let within a b p out x =
string out a; string out a;
@ -131,17 +118,17 @@ let map f pp fmt x =
pp fmt (f x); pp fmt (f x);
() ()
let vbox ?(i=0) pp out x = let vbox ?(i = 0) pp out x =
Format.pp_open_vbox out i; Format.pp_open_vbox out i;
pp out x; pp out x;
Format.pp_close_box out () Format.pp_close_box out ()
let hovbox ?(i=0) pp out x = let hovbox ?(i = 0) pp out x =
Format.pp_open_hovbox out i; Format.pp_open_hovbox out i;
pp out x; pp out x;
Format.pp_close_box out () Format.pp_close_box out ()
let hvbox ?(i=0) pp out x = let hvbox ?(i = 0) pp out x =
Format.pp_open_hvbox out i; Format.pp_open_hvbox out i;
pp out x; pp out x;
Format.pp_close_box out () Format.pp_close_box out ()
@ -152,13 +139,23 @@ let hbox pp out x =
Format.pp_close_box out () Format.pp_close_box out ()
let of_to_string f out x = Format.pp_print_string out (f x) let of_to_string f out x = Format.pp_print_string out (f x)
let exn = of_to_string Printexc.to_string
let const pp x out () = pp out x let const pp x out () = pp out x
let some pp out = function let some pp out = function
| None -> () | None -> ()
| Some x -> pp out x | Some x -> pp out x
let const_string s out _ = string out s
let opaque out _ = string out "opaque"
let lazy_force pp out (lazy x) = pp out x
let lazy_or ?(default = return "<lazy>") pp out x =
if Lazy.is_val x then
pp out (Lazy.force x)
else
default out ()
(** {2 IO} *) (** {2 IO} *)
let output fmt pp x = pp fmt x let output fmt pp x = pp fmt x
@ -171,10 +168,8 @@ let to_string pp x =
Buffer.contents buf Buffer.contents buf
let fprintf = Format.fprintf let fprintf = Format.fprintf
let stdout = Format.std_formatter let stdout = Format.std_formatter
let stderr = Format.err_formatter let stderr = Format.err_formatter
let of_chan = Format.formatter_of_out_channel let of_chan = Format.formatter_of_out_channel
let with_out_chan oc f = let with_out_chan oc f =
@ -192,134 +187,184 @@ let tee a b =
let fb = Format.pp_get_formatter_out_functions b () in let fb = Format.pp_get_formatter_out_functions b () in
Format.make_formatter Format.make_formatter
(fun str i len -> (fun str i len ->
fa.Format.out_string str i len; fa.Format.out_string str i len;
fb.Format.out_string str i len) fb.Format.out_string str i len)
(fun () -> fa.Format.out_flush (); fb.Format.out_flush ()) (fun () ->
fa.Format.out_flush ();
(*$R fb.Format.out_flush ())
let buf1 = Buffer.create 42 in
let buf2 = Buffer.create 42 in
let f1 = Format.formatter_of_buffer buf1 in
let f2 = Format.formatter_of_buffer buf2 in
let fmt = tee f1 f2 in
Format.fprintf fmt "coucou@.";
assert_equal ~printer:CCFun.id "coucou\n" (Buffer.contents buf1);
assert_equal ~printer:CCFun.id "coucou\n" (Buffer.contents buf2);
*)
let to_file filename format = let to_file filename format =
let oc = open_out filename in let oc = open_out filename in
let fmt = Format.formatter_of_out_channel oc in let fmt = Format.formatter_of_out_channel oc in
Format.kfprintf Format.kfprintf
(fun fmt -> Format.pp_print_flush fmt (); close_out_noerr oc) (fun fmt ->
Format.pp_print_flush fmt ();
close_out_noerr oc)
fmt format fmt format
type color = module ANSI_codes = struct
[ `Black type color =
| `Red [ `Black
| `Yellow | `Red
| `Green | `Yellow
| `Blue | `Green
| `Magenta | `Blue
| `Cyan | `Magenta
| `White | `Cyan
] | `White
]
let int_of_color_ = function let int_of_color_ = function
| `Black -> 0 | `Black -> 0
| `Red -> 1 | `Red -> 1
| `Green -> 2 | `Green -> 2
| `Yellow -> 3 | `Yellow -> 3
| `Blue -> 4 | `Blue -> 4
| `Magenta -> 5 | `Magenta -> 5
| `Cyan -> 6 | `Cyan -> 6
| `White -> 7 | `White -> 7
type style = type style =
[ `FG of color (* foreground *) [ `FG of color (* foreground *)
| `BG of color (* background *) | `BG of color (* background *)
| `Bold | `Bold
| `Reset | `Reset
] ]
let code_of_style : style -> int = function let code_of_style : style -> int = function
| `FG c -> 30 + int_of_color_ c | `FG c -> 30 + int_of_color_ c
| `BG c -> 40 + int_of_color_ c | `BG c -> 40 + int_of_color_ c
| `Bold -> 1 | `Bold -> 1
| `Reset -> 0 | `Reset -> 0
let ansi_l_to_str_ = function let string_of_style a = Printf.sprintf "\x1b[%dm" (code_of_style a)
| [] -> "\x1b[0m" let clear_line = "\x1b[2K\r"
| [a] -> Printf.sprintf "\x1b[%dm" (code_of_style a) let reset = string_of_style `Reset
| [a;b] -> Printf.sprintf "\x1b[%d;%dm" (code_of_style a) (code_of_style b)
| l ->
let buf = Buffer.create 16 in
let pp_num c = Buffer.add_string buf (string_of_int (code_of_style c)) in
Buffer.add_string buf "\x1b[";
List.iteri
(fun i c ->
if i>0 then Buffer.add_char buf ';';
pp_num c)
l;
Buffer.add_string buf "m";
Buffer.contents buf
(* parse a tag *) let string_of_style_list = function
let style_of_tag_ s = match String.trim s with | [] -> reset
| "reset" -> [`Reset] | [ a ] -> string_of_style a
| "black" -> [`FG `Black] | [ a; b ] ->
| "red" -> [`FG `Red] Printf.sprintf "\x1b[%d;%dm" (code_of_style a) (code_of_style b)
| "green" -> [`FG `Green] | [ a; b; c ] ->
| "yellow" -> [`FG `Yellow] Printf.sprintf "\x1b[%d;%d;%dm" (code_of_style a) (code_of_style b)
| "blue" -> [`FG `Blue] (code_of_style c)
| "magenta" -> [`FG `Magenta] | l ->
| "cyan" -> [`FG `Cyan] let buf = Buffer.create 32 in
| "white" -> [`FG `White] let pp_num c = Buffer.add_string buf (string_of_int (code_of_style c)) in
| "bold" -> [`Bold] Buffer.add_string buf "\x1b[";
| "Black" -> [`FG `Black; `Bold] List.iteri
| "Red" -> [`FG `Red; `Bold] (fun i c ->
| "Green" -> [`FG `Green; `Bold] if i > 0 then Buffer.add_char buf ';';
| "Yellow" -> [`FG `Yellow; `Bold] pp_num c)
| "Blue" -> [`FG `Blue; `Bold] l;
| "Magenta" -> [`FG `Magenta; `Bold] Buffer.add_string buf "m";
| "Cyan" -> [`FG `Cyan; `Bold] Buffer.contents buf
| "White" -> [`FG `White; `Bold]
| s -> failwith ("unknown style: " ^ s) exception No_such_style
(* parse a string tag. *)
let style_of_tag_ s =
match String.trim s with
| "reset" -> [ `Reset ]
| "black" -> [ `FG `Black ]
| "red" -> [ `FG `Red ]
| "green" -> [ `FG `Green ]
| "yellow" -> [ `FG `Yellow ]
| "blue" -> [ `FG `Blue ]
| "magenta" -> [ `FG `Magenta ]
| "cyan" -> [ `FG `Cyan ]
| "white" -> [ `FG `White ]
| "bold" -> [ `Bold ]
| "Black" -> [ `FG `Black; `Bold ]
| "Red" -> [ `FG `Red; `Bold ]
| "Green" -> [ `FG `Green; `Bold ]
| "Yellow" -> [ `FG `Yellow; `Bold ]
| "Blue" -> [ `FG `Blue; `Bold ]
| "Magenta" -> [ `FG `Magenta; `Bold ]
| "Cyan" -> [ `FG `Cyan; `Bold ]
| "White" -> [ `FG `White; `Bold ]
| _ -> raise No_such_style
end
let color_enabled = ref false let color_enabled = ref false
(* either prints the tag of [s] or delegate to [or_else] *) let mark_open_style st style =
let mark_open_tag st ~or_else s = Stack.push style st;
try if !color_enabled then
let style = style_of_tag_ s in ANSI_codes.string_of_style_list style
Stack.push style st; else
if !color_enabled then ansi_l_to_str_ style else "" ""
with Not_found -> or_else s
let mark_close_tag st ~or_else s = let mark_close_style st : string =
let style =
try
ignore (Stack.pop st);
(* pop current style (if well-scoped …) *)
Stack.top st
(* look at previous style *)
with Stack.Empty -> [ `Reset ]
in
if !color_enabled then
ANSI_codes.string_of_style_list style
else
""
type stag += Style of ANSI_codes.style list
let pp_open_tag out s = pp_open_stag out (String_tag s)
let pp_close_tag out () = pp_close_stag out ()
(* either prints the tag of [s] or delegate to [or_else] *)
let mark_open_stag st ~or_else (tag : stag) : string =
match tag with
| Style style -> mark_open_style st style
| String_tag s ->
let open ANSI_codes in
(try
let style = style_of_tag_ s in
mark_open_style st style
with No_such_style -> or_else tag)
| _ -> or_else tag
let mark_close_stag st ~or_else (tag : stag) : string =
match tag with
| Style _ -> mark_close_style st
| String_tag s ->
let open ANSI_codes in
(* check if it's indeed about color *)
(match style_of_tag_ s with
| _ -> mark_close_style st
| exception No_such_style -> or_else tag)
| _ -> or_else tag
let with_styling stl out f =
pp_open_stag out (Style stl);
try try
let _ = style_of_tag_ s in (* check if it's indeed about color *) let x = f () in
let style = pp_close_stag out ();
try x
ignore (Stack.pop st); (* pop current style (if well-scoped...) *) with e ->
Stack.top st (* look at previous style *) pp_close_stag out ();
with Stack.Empty -> raise e
[`Reset]
in let styling stl pp out x = with_styling stl out @@ fun () -> pp out x
if !color_enabled then ansi_l_to_str_ style else ""
with Not_found -> or_else s
(* add color handling to formatter [ppf] *) (* add color handling to formatter [ppf] *)
let set_color_tag_handling ppf = let set_color_tag_handling ppf =
let open Format in let st = Stack.create () in
let functions = pp_get_formatter_tag_functions ppf () in (* stack of styles *)
let st = Stack.create () in (* stack of styles *) pp_set_mark_tags ppf true;
let functions' = {functions with (* enable tags *)
mark_open_tag=(mark_open_tag st ~or_else:functions.mark_open_tag); let funs = pp_get_formatter_stag_functions ppf () in
mark_close_tag=(mark_close_tag st ~or_else:functions.mark_close_tag); let funs' =
} in {
pp_set_mark_tags ppf true; (* enable tags *) funs with
pp_set_formatter_tag_functions ppf functions' mark_open_stag = mark_open_stag st ~or_else:funs.mark_open_stag;
mark_close_stag = mark_close_stag st ~or_else:funs.mark_close_stag;
}
in
pp_set_formatter_stag_functions ppf funs'
let set_color_default = let set_color_default =
let first = ref true in let first = ref true in
@ -329,30 +374,19 @@ let set_color_default =
if !first then ( if !first then (
first := false; first := false;
set_color_tag_handling stdout; set_color_tag_handling stdout;
set_color_tag_handling stderr; set_color_tag_handling stderr
); )
) else if not b && !color_enabled then color_enabled := false ) else if (not b) && !color_enabled then
color_enabled := false
(*$R
set_color_default true;
let s = sprintf
"what is your @{<White>favorite color@}? @{<blue>blue@}! No, @{<red>red@}! Ahhhhhhh@."
in
assert_equal ~printer:CCFun.id
"what is your \027[37;1mfavorite color\027[0m? \027[34mblue\027[0m! No, \027[31mred\027[0m! Ahhhhhhh\n"
s
*)
let with_color s pp out x = let with_color s pp out x =
Format.pp_open_tag out s; pp_open_tag out s;
pp out x; pp out x;
Format.pp_close_tag out () pp_close_tag out ()
let with_colorf s out fmt = let with_colorf s out fmt =
Format.pp_open_tag out s; pp_open_tag out s;
Format.kfprintf Format.kfprintf (fun out -> pp_close_tag out ()) out fmt
(fun out -> Format.pp_close_tag out ())
out fmt
(* c: whether colors are enabled *) (* c: whether colors are enabled *)
let sprintf_ c format = let sprintf_ c format =
@ -360,58 +394,50 @@ let sprintf_ c format =
let fmt = Format.formatter_of_buffer buf in let fmt = Format.formatter_of_buffer buf in
if c && !color_enabled then set_color_tag_handling fmt; if c && !color_enabled then set_color_tag_handling fmt;
Format.kfprintf Format.kfprintf
(fun _fmt -> Format.pp_print_flush fmt (); Buffer.contents buf) (fun _fmt ->
fmt Format.pp_print_flush fmt ();
format Buffer.contents buf)
fmt format
let with_color_ksf ~f s fmt = let with_color_ksf ~f s fmt =
let buf = Buffer.create 64 in let buf = Buffer.create 64 in
let out = Format.formatter_of_buffer buf in let out = Format.formatter_of_buffer buf in
if !color_enabled then set_color_tag_handling out; if !color_enabled then set_color_tag_handling out;
Format.pp_open_tag out s; pp_open_tag out s;
Format.kfprintf Format.kfprintf
(fun out -> (fun out ->
Format.pp_close_tag out (); pp_close_tag out ();
Format.pp_print_flush out (); Format.pp_print_flush out ();
f (Buffer.contents buf)) f (Buffer.contents buf))
out fmt out fmt
let with_color_sf s fmt = with_color_ksf ~f:(fun s->s) s fmt let with_color_sf s fmt = with_color_ksf ~f:(fun s -> s) s fmt
let sprintf fmt = sprintf_ true fmt let sprintf fmt = sprintf_ true fmt
let sprintf_no_color fmt = sprintf_ false fmt let sprintf_no_color fmt = sprintf_ false fmt
let sprintf_dyn_color ~colors fmt = sprintf_ colors fmt let sprintf_dyn_color ~colors fmt = sprintf_ colors fmt
let fprintf_dyn_color ~colors out fmt = let fprintf_dyn_color ~colors out fmt =
let old_tags = Format.pp_get_mark_tags out () in let old_tags = Format.pp_get_mark_tags out () in
Format.pp_set_mark_tags out colors; (* enable/disable tags *) Format.pp_set_mark_tags out colors;
Format.kfprintf (* enable/disable tags *)
(fun out -> Format.pp_set_mark_tags out old_tags) Format.kfprintf (fun out -> Format.pp_set_mark_tags out old_tags) out fmt
out fmt
(*$T let ksprintf ?margin ~f fmt =
sprintf "yolo %s %d" "a b" 42 = "yolo a b 42"
sprintf "%d " 0 = "0 "
sprintf_no_color "%d " 0 = "0 "
*)
(*$R
set_color_default true;
assert_equal "\027[31myolo\027[0m" (sprintf "@{<red>yolo@}");
assert_equal "yolo" (sprintf_no_color "@{<red>yolo@}");
*)
let ksprintf ~f fmt =
let buf = Buffer.create 32 in let buf = Buffer.create 32 in
let out = Format.formatter_of_buffer buf in let out = Format.formatter_of_buffer buf in
if !color_enabled then set_color_tag_handling out; if !color_enabled then set_color_tag_handling out;
(match margin with
| None -> ()
| Some m -> pp_set_margin out m);
Format.kfprintf Format.kfprintf
(fun _ -> Format.pp_print_flush out (); f (Buffer.contents buf)) (fun _ ->
Format.pp_print_flush out ();
f (Buffer.contents buf))
out fmt out fmt
module Dump = struct module Dump = struct
type 'a t = 'a printer type 'a t = 'a printer
let unit = unit let unit = unit
let int = int let int = int
let string = string_quoted let string = string_quoted
@ -423,23 +449,26 @@ module Dump = struct
let nativeint = nativeint let nativeint = nativeint
let list pp = within "[" "]" (hovbox (list ~sep:(return ";@,") pp)) let list pp = within "[" "]" (hovbox (list ~sep:(return ";@,") pp))
let array pp = within "[|" "|]" (hovbox (array ~sep:(return ";@,") pp)) let array pp = within "[|" "|]" (hovbox (array ~sep:(return ";@,") pp))
let option pp out x = match x with
let option pp out x =
match x with
| None -> Format.pp_print_string out "None" | None -> Format.pp_print_string out "None"
| Some x -> Format.fprintf out "Some %a" pp x | Some x -> Format.fprintf out "Some %a" pp x
let pair p1 p2 = within "(" ")" (hovbox (pair p1 p2)) let pair p1 p2 = within "(" ")" (hovbox (pair p1 p2))
let triple p1 p2 p3 = within "(" ")" (hovbox (triple p1 p2 p3)) let triple p1 p2 p3 = within "(" ")" (hovbox (triple p1 p2 p3))
let quad p1 p2 p3 p4 = within "(" ")" (hovbox (quad p1 p2 p3 p4)) let quad p1 p2 p3 p4 = within "(" ")" (hovbox (quad p1 p2 p3 p4))
let result' pok perror out = function let result' pok perror out = function
| Result.Ok x -> Format.fprintf out "(@[Ok %a@])" pok x | Ok x -> Format.fprintf out "(@[Ok %a@])" pok x
| Result.Error e -> Format.fprintf out "(@[Error %a@])" perror e | Error e -> Format.fprintf out "(@[Error %a@])" perror e
let result pok = result' pok string let result pok = result' pok string
let to_string = to_string let to_string = to_string
end end
(*$= & ~printer:(fun s->s) module Infix = struct
"[1;2;3]" (to_string Dump.(list int) [1;2;3]) let ( ++ ) = append
"Some 1" (to_string Dump.(option int) (Some 1)) end
"[None;Some \"a b\"]" (to_string Dump.(list (option string)) [None; Some "a b"])
"[(Ok \"a b c\");(Error \"nope\")]" \ include Infix
(to_string Dump.(list (result string)) [Result.Ok "a b c"; Result.Error "nope"])
*)

View file

@ -1,21 +1,30 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Helpers for Format} (** Helpers for Format
@since 0.8 *) @since 0.8 *)
type 'a sequence = ('a -> unit) -> unit type 'a iter = ('a -> unit) -> unit
(* include Format, and alias all its types.
see https://discuss.ocaml.org/t/extend-existing-module/1389/4
*)
(** @inline *)
include module type of struct
include Format
end
type t = Format.formatter type t = Format.formatter
type 'a printer = t -> 'a -> unit type -'a printer = t -> 'a -> unit
(** {2 Combinators} *) (** {2 Combinators} *)
val silent : 'a printer (** Prints nothing *) val silent : 'a printer
(** Prints nothing. *)
val unit : unit printer val unit : unit printer
(** Prints "()" *) (** Prints "()". *)
val int : int printer val int : int printer
val string : string printer val string : string printer
@ -23,12 +32,28 @@ val bool : bool printer
val float3 : float printer (* 3 digits after . *) val float3 : float printer (* 3 digits after . *)
val float : float printer val float : float printer
val exn : exn printer
(** Printer using {!Printexc.to_string}.
@since 3.0 *)
val space : unit printer
(** Alias to {!pp_print_space}.
@since 3.2 *)
val cut : unit printer
(** Alias to {!pp_print_cut}.
@since 3.2 *)
val break : (int * int) printer
(** Tuple-ized {!printer} form of {!pp_print_break}.
@since 3.2 *)
val newline : unit printer val newline : unit printer
(** Force newline (see {!Format.pp_force_newline}) (** Force newline (see {!Format.pp_force_newline}).
@since 1.2 *) @since 1.2 *)
val substring : (string * int * int) printer val substring : (string * int * int) printer
(** Print the substring [(s,i,len)], where [i] is the offset (** [substring (s,i,len)] prints the substring [(s,i,len)], where [i] is the offset
in [s] and [len] the number of bytes in the substring. in [s] and [len] the number of bytes in the substring.
@raise Invalid_argument if the triple [(s,i,len)] does not @raise Invalid_argument if the triple [(s,i,len)] does not
describe a proper substring. describe a proper substring.
@ -40,10 +65,25 @@ val text : string printer
See [pp_print_text] on recent versions of OCaml. See [pp_print_text] on recent versions of OCaml.
@since 1.2 *) @since 1.2 *)
val char : char printer (** @since 0.14 *) val string_lines : string printer
val int32 : int32 printer (** @since 0.14 *) (** [string_lines out s] prints [s] with all newlines (['\n']) replaced by
val int64 : int64 printer (** @since 0.14 *) a cut, in a vertical box. It does {b NOT} insert breakable spaces in
val nativeint : nativeint printer (** @since 0.14 *) place of spaces, unlike {!text}.
This means an already formatted string can be displayed inside another
formatter without mangling the indentation.
@since 3.3 *)
val char : char printer
(** @since 0.14 *)
val int32 : int32 printer
(** @since 0.14 *)
val int64 : int64 printer
(** @since 0.14 *)
val nativeint : nativeint printer
(** @since 0.14 *)
val flush : unit printer val flush : unit printer
(** Alias to {!Format.pp_print_flush}. (** Alias to {!Format.pp_print_flush}.
@ -56,20 +96,41 @@ val string_quoted : string printer
val list : ?sep:unit printer -> 'a printer -> 'a list printer val list : ?sep:unit printer -> 'a printer -> 'a list printer
val array : ?sep:unit printer -> 'a printer -> 'a array printer val array : ?sep:unit printer -> 'a printer -> 'a array printer
val arrayi : ?sep:unit printer -> (int * 'a) printer -> 'a array printer val arrayi : ?sep:unit printer -> (int * 'a) printer -> 'a array printer
val seq : ?sep:unit printer -> 'a printer -> 'a sequence printer val seq : ?sep:unit printer -> 'a printer -> 'a Seq.t printer
val iter : ?sep:unit printer -> 'a printer -> 'a iter printer
val opt : 'a printer -> 'a option printer val opt : 'a printer -> 'a option printer
(** [opt pp] prints options as follows: (** [opt pp] prints options as follows:
[Some x] will become "some foo" if [pp x ---> "foo"] - [Some x] will become "some foo" if [pp x ---> "foo"].
[None] will become "none" *) - [None] will become "none". *)
(** In the tuple printers, the [sep] argument is only available (** In the tuple printers, the [sep] argument is only available.
@since 0.17 *) @since 0.17 *)
val pair : ?sep:unit printer -> 'a printer -> 'b printer -> ('a * 'b) printer val pair : ?sep:unit printer -> 'a printer -> 'b printer -> ('a * 'b) printer
val triple : ?sep:unit printer -> 'a printer -> 'b printer -> 'c printer -> ('a * 'b * 'c) printer
val quad : ?sep:unit printer -> 'a printer -> 'b printer -> val triple :
'c printer -> 'd printer -> ('a * 'b * 'c * 'd) printer ?sep:unit printer ->
'a printer ->
'b printer ->
'c printer ->
('a * 'b * 'c) printer
val quad :
?sep:unit printer ->
'a printer ->
'b printer ->
'c printer ->
'd printer ->
('a * 'b * 'c * 'd) printer
val append : unit printer -> unit printer -> unit printer
(** [append ppa ppb] first prints [ppa ()], then prints [ppb ()].
@since 3.2 *)
val append_l : unit printer list -> unit printer
(** [append_l pps] runs the printers in [pps] sequentially.
@since 3.2 *)
val within : string -> string -> 'a printer -> 'a printer val within : string -> string -> 'a printer -> 'a printer
(** [within a b p] wraps [p] inside the strings [a] and [b]. Convenient, (** [within a b p] wraps [p] inside the strings [a] and [b]. Convenient,
@ -79,22 +140,22 @@ val within : string -> string -> 'a printer -> 'a printer
val map : ('a -> 'b) -> 'b printer -> 'a printer val map : ('a -> 'b) -> 'b printer -> 'a printer
val vbox : ?i:int -> 'a printer -> 'a printer val vbox : ?i:int -> 'a printer -> 'a printer
(** Wrap the printer in a vertical box (** Wrap the printer in a vertical box.
@param i level of indentation within the box (default 0) @param i level of indentation within the box (default 0).
@since 0.16 *) @since 0.16 *)
val hvbox : ?i:int -> 'a printer -> 'a printer val hvbox : ?i:int -> 'a printer -> 'a printer
(** Wrap the printer in a horizontal/vertical box (** Wrap the printer in a horizontal/vertical box.
@param i level of indentation within the box (default 0) @param i level of indentation within the box (default 0).
@since 0.16 *) @since 0.16 *)
val hovbox : ?i:int -> 'a printer -> 'a printer val hovbox : ?i:int -> 'a printer -> 'a printer
(** Wrap the printer in a horizontal or vertical box (** Wrap the printer in a horizontal or vertical box.
@param i level of indentation within the box (default 0) @param i level of indentation within the box (default 0).
@since 0.16 *) @since 0.16 *)
val hbox : 'a printer -> 'a printer val hbox : 'a printer -> 'a printer
(** Wrap the printer in an horizontal box (** Wrap the printer in an horizontal box.
@since 0.16 *) @since 0.16 *)
val return : ('a, _, _, 'a) format4 -> unit printer val return : ('a, _, _, 'a) format4 -> unit printer
@ -110,11 +171,11 @@ val return : ('a, _, _, 'a) format4 -> unit printer
val of_to_string : ('a -> string) -> 'a printer val of_to_string : ('a -> string) -> 'a printer
(** [of_to_string f] converts its input to a string using [f], (** [of_to_string f] converts its input to a string using [f],
then prints the string then prints the string.
@since 1.0 *) @since 1.0 *)
val const : 'a printer -> 'a -> unit printer val const : 'a printer -> 'a -> unit printer
(** [const pp x] is a unit printer that uses [pp] on [x] (** [const pp x] is a unit printer that uses [pp] on [x].
@since 1.0 *) @since 1.0 *)
val some : 'a printer -> 'a option printer val some : 'a printer -> 'a option printer
@ -124,6 +185,25 @@ val some : 'a printer -> 'a option printer
@since 1.0 @since 1.0
*) *)
val const_string : string -> 'a printer
(** [const_string s] is a printer that ignores its input and
always prints [s].
@since 3.5 *)
val opaque : 'a printer
(** [opaque] is [const_string "opaque"].
The exact string used is not stable.
@since 3.5 *)
val lazy_force : 'a printer -> 'a lazy_t printer
(** [lazy_force pp out x] forces [x] and prints the result with [pp].
@since 2.0 *)
val lazy_or : ?default:unit printer -> 'a printer -> 'a lazy_t printer
(** [lazy_or ?default pp out x] prints [default] if [x] is not
evaluated yet, or uses [pp] otherwise.
@since 2.0 *)
(** {2 ANSI codes} (** {2 ANSI codes}
Use ANSI escape codes https://en.wikipedia.org/wiki/ANSI_escape_code Use ANSI escape codes https://en.wikipedia.org/wiki/ANSI_escape_code
@ -166,7 +246,7 @@ val some : 'a printer -> 'a option printer
@since 0.15 *) @since 0.15 *)
val set_color_tag_handling : t -> unit val set_color_tag_handling : t -> unit
(** adds functions to support color tags to the given formatter. (** Add functions to support color tags to the given formatter.
@since 0.15 *) @since 0.15 *)
val set_color_default : bool -> unit val set_color_default : bool -> unit
@ -177,28 +257,34 @@ val set_color_default : bool -> unit
val with_color : string -> 'a printer -> 'a printer val with_color : string -> 'a printer -> 'a printer
(** [with_color "Blue" pp] behaves like the printer [pp], but with the given (** [with_color "Blue" pp] behaves like the printer [pp], but with the given
style. style.
{b status: unstable} {b status: unstable}
@since 0.16 *) @since 0.16 *)
val with_colorf : string -> t -> ('a, t, unit, unit) format4 -> 'a val with_colorf : string -> t -> ('a, t, unit, unit) format4 -> 'a
(** [with_colorf "Blue" out "%s %d" "yolo" 42] will behave like {!Format.fprintf}, (** [with_colorf "Blue" out "%s %d" "yolo" 42] will behave like {!Format.fprintf},
but wrapping the content with the given style but wrapping the content with the given style.
{b status: unstable} {b status: unstable}
@since 0.16 *) @since 0.16 *)
val with_color_sf : string -> ('a, t, unit, string) format4 -> 'a val with_color_sf : string -> ('a, t, unit, string) format4 -> 'a
(** [with_color_sf "Blue" out "%s %d" "yolo" 42] will behave like (** [with_color_sf "Blue" out "%s %d" "yolo" 42] will behave like
{!sprintf}, but wrapping the content with the given style {!sprintf}, but wrapping the content with the given style.
Example: Example:
{[ {[
CCFormat.with_color_sf "red" "%a" CCFormat.Dump.(list int) [1;2;3] |> print_endline;; CCFormat.with_color_sf "red" "%a" CCFormat.Dump.(list int) [1;2;3] |> print_endline;;
]} ]}
{b status: unstable} {b status: unstable}
@since 0.21 *) @since 0.21 *)
val with_color_ksf : f:(string -> 'b) -> string -> ('a, t, unit, 'b) format4 -> 'a val with_color_ksf :
f:(string -> 'b) -> string -> ('a, t, unit, 'b) format4 -> 'a
(** [with_color_ksf "Blue" ~f "%s %d" "yolo" 42] will behave like (** [with_color_ksf "Blue" ~f "%s %d" "yolo" 42] will behave like
{!ksprintf}, but wrapping the content with the given style {!ksprintf}, but wrapping the content with the given style.
Example: Example:
the following with raise [Failure] with a colored message the following with raise [Failure] with a colored message
{[ {[
@ -206,13 +292,90 @@ val with_color_ksf : f:(string -> 'b) -> string -> ('a, t, unit, 'b) format4 ->
]} ]}
@since 1.2 *) @since 1.2 *)
(** ANSI escape codes. This contains lower level functions for them.
@since 3.5 *)
module ANSI_codes : sig
type color =
[ `Black
| `Red
| `Yellow
| `Green
| `Blue
| `Magenta
| `Cyan
| `White
]
(** An ANSI color *)
type style =
[ `FG of color (** foreground *)
| `BG of color (** background *)
| `Bold
| `Reset
]
(** A style. Styles can be composed in a list. *)
val clear_line : string
(** [clear_line] is an escape code to clear the current line. It
is very useful for progress bars; for example:
{[
let pp_progress i =
Printf.printf "%sprogress at %d%!" ANSI_codes.clear_line i
]}
if called repeatedly this will print successive progress messages
on a single line.
*)
val reset : string
(** The escape code to reset style (colors, bold, etc.) *)
val string_of_style : style -> string
(** [string_of_style st] is an escape code to set the current style
to [st]. It can be printed as is on any output that is a
compatible terminal. *)
val string_of_style_list : style list -> string
(** [string_of_style_list styles] is an escape code
for multiple styles at once.
For example [string_of_style_list ANSI_codes.([`FG `Red; `BG `Green; `Bold])]
is a very shiny style. *)
end
val styling : ANSI_codes.style list -> 'a printer -> 'a printer
(** [styling st p] is the same printer as [p], except it locally sets
the style [st].
Example:
{[
open CCFormat;
set_color_default true;
sprintf
"what is your %a? %a! No, %a! Ahhhhhhh@."
(styling [`FG `White; `Bold] string) "favorite color"
(styling [`FG `Blue] string) "blue"
(styling [`FG `Red] string) "red"
]}
Available only on OCaml >= 4.08.
@since 3.7 *)
val with_styling : ANSI_codes.style list -> t -> (unit -> 'a) -> 'a
(** [with_styling style fmt f] sets the given style on [fmt],
calls [f()], then restores the previous style.
It is useful in imperative-style printers (a sequence of "print a; print b; …").
Available only on OCaml >= 4.08.
@since 3.7 *)
(** {2 IO} *) (** {2 IO} *)
val output : t -> 'a printer -> 'a -> unit val output : t -> 'a printer -> 'a -> unit
val to_string : 'a printer -> 'a -> string val to_string : 'a printer -> 'a -> string
val of_chan : out_channel -> t val of_chan : out_channel -> t
(** Alias to {!Format.formatter_of_out_channel} (** Alias to {!Format.formatter_of_out_channel}.
@since 1.2 *) @since 1.2 *)
val with_out_chan : out_channel -> (t -> 'a) -> 'a val with_out_chan : out_channel -> (t -> 'a) -> 'a
@ -230,14 +393,15 @@ val tee : t -> t -> t
val sprintf : ('a, t, unit, string) format4 -> 'a val sprintf : ('a, t, unit, string) format4 -> 'a
(** Print into a string any format string that would usually be compatible (** Print into a string any format string that would usually be compatible
with {!fprintf}. Similar to {!Format.asprintf}. *) with {!fprintf}. Like {!Format.asprintf}. *)
val sprintf_no_color : ('a, t, unit, string) format4 -> 'a val sprintf_no_color : ('a, t, unit, string) format4 -> 'a
(** Similar to {!sprintf} but never prints colors (** Like {!sprintf} but never prints colors.
@since 0.16 *) @since 0.16 *)
val sprintf_dyn_color : colors:bool -> ('a, t, unit, string) format4 -> 'a val sprintf_dyn_color : colors:bool -> ('a, t, unit, string) format4 -> 'a
(** Similar to {!sprintf} but enable/disable colors depending on [colors]. (** Like {!sprintf} but enable/disable colors depending on [colors].
Example: Example:
{[ {[
(* with colors *) (* with colors *)
@ -250,29 +414,26 @@ val sprintf_dyn_color : colors:bool -> ('a, t, unit, string) format4 -> 'a
]} ]}
@since 0.21 *) @since 0.21 *)
val fprintf : t -> ('a, t, unit ) format -> 'a val fprintf : t -> ('a, t, unit) format -> 'a
(** Alias to {!Format.fprintf} (** Alias to {!Format.fprintf}.
@since 0.14 *) @since 0.14 *)
val fprintf_dyn_color : colors:bool -> 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] (** Like {!fprintf} but enable/disable colors depending on [colors].
@since 0.21 *) @since 0.21 *)
val ksprintf : val ksprintf :
?margin:int ->
f:(string -> 'b) -> f:(string -> 'b) ->
('a, Format.formatter, unit, 'b) format4 -> ('a, Format.formatter, unit, 'b) format4 ->
'a 'a
(** [ksprintf fmt ~f] formats using [fmt], in a way similar to {!sprintf}, (** [ksprintf fmt ~f] formats using [fmt], in a way similar to {!sprintf},
and then calls [f] on the resulting string. and then calls [f] on the resulting string.
@param margin set margin (since 2.1)
@since 0.14 *) @since 0.14 *)
(*$= & ~printer:CCFormat.(to_string (opt string))
(Some "hello world") \
(ksprintf "hello %a" CCFormat.string "world" ~f:(fun s -> Some s))
*)
val to_file : string -> ('a, t, unit, unit) format4 -> 'a val to_file : string -> ('a, t, unit, unit) format4 -> 'a
(** Print to the given file *) (** Print to the given file. *)
(** {2 Dump} (** {2 Dump}
@ -291,6 +452,7 @@ val to_file : string -> ('a, t, unit, unit) format4 -> 'a
module Dump : sig module Dump : sig
type 'a t = 'a printer type 'a t = 'a printer
val unit : unit t val unit : unit t
val int : int t val int : int t
val string : string t val string : string t
@ -305,11 +467,18 @@ module Dump : sig
val option : 'a t -> 'a option t val option : 'a t -> 'a option t
val pair : 'a t -> 'b t -> ('a * 'b) t val pair : 'a t -> 'b t -> ('a * 'b) t
val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
val quad : val quad : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t
'a t -> 'b t -> 'c t -> 'd t -> val result : 'a t -> ('a, string) result t
('a * 'b * 'c * 'd) t val result' : 'a t -> 'e t -> ('a, 'e) result t
val result : 'a t -> ('a, string) Result.result t
val result' : 'a t -> 'e t -> ('a, 'e) Result.result t
val to_string : 'a t -> 'a -> string val to_string : 'a t -> 'a -> string
(** Alias to {!CCFormat.to_string} *) (** Alias to {!CCFormat.to_string}. *)
end end
module Infix : sig
val ( ++ ) : unit printer -> unit printer -> unit printer
(** Alias to {!append}.
@since 3.2 *)
end
include module type of Infix

View file

@ -1,43 +1,30 @@
(* This file is free software, part of containers. See file "license" for more details. *) (* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Basic Functions} *) (** {1 Basic Functions} *)
(* default implem for some operators *)
let (|>) x f = f x
let (@@) f x = f x
let opaque_identity x = x
(* import standard implementations, if any *) (* import standard implementations, if any *)
include Sys include Sys
include Pervasives include Stdlib
include Fun
let compose f g x = g (f x) let[@inline] and_pred f g x = f x && g x
let[@inline] or_pred f g x = f x || g x
let[@inline] compose f g x = g (f x)
let[@inline] compose_binop f g x y = g (f x) (f y)
let[@inline] curry f x y = f (x, y)
let[@inline] uncurry f (x, y) = f x y
let compose_binop f g x y = g (f x) (f y) let[@inline] tap f x =
ignore (f x);
let flip f x y = f y x x
let curry f x y = f (x,y)
let id x = x
let const x _ = x
let uncurry f (x,y) = f x y
let tap f x = ignore (f x); x
let (%>) = compose
let (%) f g x = f (g x)
let lexicographic f1 f2 x y = let lexicographic f1 f2 x y =
let c = f1 x y in let c = f1 x y in
if c <> 0 then c else f2 x y if c <> 0 then
c
else
f2 x y
let finally ~h ~f = let finally ~h ~f =
try try
@ -66,9 +53,40 @@ let finally2 ~h f x y =
ignore (h ()); ignore (h ());
raise e raise e
module Monad(X : sig type t end) = struct let rec iterate n f x =
type 'a t = X.t -> 'a if n < 0 then
let return x _ = x invalid_arg "CCFun.iterate"
let (>|=) f g x = g (f x) else if n = 0 then
let (>>=) f g x = g (f x) x x
else
iterate (n - 1) f (f x)
let[@inline] with_return (type ret) f : ret =
let exception E of ret in
let return x = raise_notrace (E x) in
match f return with
| res -> res
| exception E res -> res
module Infix = struct
(* default implem for some operators *)
let ( %> ) = compose
let[@inline] ( % ) f g x = f (g x)
let ( let@ ) = ( @@ )
let ( ||> ) (a, b) f = f a b
let ( |||> ) (a, b, c) f = f a b c
end end
include Infix
module Monad (X : sig
type t
end) =
struct
type 'a t = X.t -> 'a
let[@inline] return x _ = x
let[@inline] ( >|= ) f g x = g (f x)
let[@inline] ( >>= ) f g x = g (f x) x
end
[@@inline]

Some files were not shown because too many files have changed in this diff Show more