Compare commits

...
483 Commits
Author SHA1 Message Date
Antonio SJ Musumeci b36fa27ebf func_create_ff.cpp 2024-03-10 18:00:29 -05:00
Antonio SJ Musumeci 782ed152ec branch2.cpp 2024-03-10 17:59:59 -05:00
Antonio SJ Musumeci b506f52ccf branch2.cpp 2024-03-10 17:59:25 -05:00
Antonio SJ Musumeci aa90e25c17 branch2.cpp 2024-03-10 17:59:24 -05:00
Antonio SJ Musumeci 7779c1e149 branch2.cpp 2024-03-10 17:59:18 -05:00
Antonio SJ Musumeci 98f60c1ae9 branch2.cpp 2024-03-10 17:58:17 -05:00
Antonio SJ Musumeci 771b04e0d7 branch2.hpp 2024-03-10 17:56:19 -05:00
Antonio SJ Musumeci 46b55fa65b branch2.hpp 2024-03-10 17:55:59 -05:00
Antonio SJ Musumeci 277af1e1b6 branch2.cpp 2024-03-10 17:54:21 -05:00
Antonio SJ Musumeci 6534d72f8c branch2.hpp 2024-03-10 17:53:53 -05:00
Antonio SJ Musumeci 67f53ea27c branch2.hpp 2024-03-10 17:53:50 -05:00
Antonio SJ Musumeci 6fd2662e04 func_create_ff.cpp 2024-03-10 17:53:13 -05:00
Antonio SJ Musumeci 3931cc8bc8 func_create_ff.cpp 2024-03-10 17:51:45 -05:00
Antonio SJ Musumeci a8d6ea8dbf func_create_ff.cpp 2024-03-10 17:48:34 -05:00
Antonio SJ Musumeci dbb1cc596f func_create_ff.cpp 2024-03-10 17:44:21 -05:00
Antonio SJ Musumeci 94ce78e406 branch2.cpp 2024-03-10 17:39:30 -05:00
Antonio SJ Musumeci 00e8eab0ef branch2.cpp 2024-03-10 17:38:05 -05:00
Antonio SJ Musumeci 4efd800771 branch2.cpp 2024-03-10 17:38:02 -05:00
Antonio SJ Musumeci 83cef6b4e4 branch2.cpp 2024-03-10 17:35:07 -05:00
Antonio SJ Musumeci 3a1dba4320 branch_tier.cpp 2024-03-10 17:34:40 -05:00
Antonio SJ Musumeci 4dc3793e2d branch2.cpp 2024-03-10 17:34:09 -05:00
Antonio SJ Musumeci 4144f60f27 branch2.cpp 2024-03-10 17:32:28 -05:00
Antonio SJ Musumeci 8d0e2f7cbb branch2.hpp 2024-03-10 17:31:53 -05:00
Antonio SJ Musumeci 1a41aa8548 branch2.cpp 2024-03-10 17:31:16 -05:00
Antonio SJ Musumeci 0458390b2e branch2.cpp 2024-03-10 17:30:25 -05:00
Antonio SJ Musumeci 1232ae5a32 branch2.hpp 2024-03-10 17:29:17 -05:00
Antonio SJ Musumeci fca16b29dc branch2.hpp 2024-03-10 17:28:55 -05:00
Antonio SJ Musumeci 8adc5c943f branch2.hpp 2024-03-10 17:28:30 -05:00
Antonio SJ Musumeci c62212a797 branch2.cpp 2024-03-10 17:27:52 -05:00
Antonio SJ Musumeci fe57f6f2d7 branch2.cpp 2024-03-10 17:27:44 -05:00
Antonio SJ Musumeci aaa13d22d7 branch2.cpp 2024-03-10 17:27:37 -05:00
Antonio SJ Musumeci 6e7b9cd452 branch_tier.cpp 2024-03-10 17:27:11 -05:00
Antonio SJ Musumeci 4495bdfc91 branch_tier.cpp 2024-03-10 17:27:00 -05:00
Antonio SJ Musumeci b7ceb1e083 branch_tier.cpp 2024-03-10 17:26:52 -05:00
Antonio SJ Musumeci 83df68e66a branch_tier.cpp 2024-03-10 17:26:26 -05:00
Antonio SJ Musumeci 6c55517a08 branch_tier.cpp 2024-03-10 17:26:06 -05:00
Antonio SJ Musumeci cf0cd7e640 branch_tier.cpp 2024-03-10 17:25:54 -05:00
Antonio SJ Musumeci 7f6ffb2738 branch_tier.cpp 2024-03-10 17:24:59 -05:00
Antonio SJ Musumeci 194456759d branch_tier.cpp 2024-03-10 17:24:26 -05:00
Antonio SJ Musumeci 9f4e1c13fe branch_tier.cpp 2024-03-10 17:24:24 -05:00
Antonio SJ Musumeci 86b7ef03bf branch_tier.cpp 2024-03-10 17:24:19 -05:00
Antonio SJ Musumeci 88c9813e47 branch_tier.hpp 2024-03-10 17:24:04 -05:00
Antonio SJ Musumeci 200b54baf7 branch_tier.hpp 2024-03-10 17:23:54 -05:00
Antonio SJ Musumeci 456d33fc14 branch_tier.hpp 2024-03-10 17:23:39 -05:00
Antonio SJ Musumeci 7cd37e81f2 branches2.cpp 2024-03-10 17:23:13 -05:00
Antonio SJ Musumeci ab88240bf2 branches2.cpp 2024-03-10 17:23:09 -05:00
Antonio SJ Musumeci e4f045060c branches2.hpp 2024-03-10 17:22:59 -05:00
Antonio SJ Musumeci d9dde798fa branches2.cpp 2024-03-10 17:22:37 -05:00
Antonio SJ Musumeci 3da6b5ae28 branches2.cpp 2024-03-10 17:08:05 -05:00
Antonio SJ Musumeci 959420e147 branches2.hpp 2024-03-10 17:06:07 -05:00
Antonio SJ Musumeci ec45b4dd95 branches2.hpp 2024-03-10 17:06:06 -05:00
Antonio SJ Musumeci 8268be34e2 branches2.hpp 2024-03-10 17:04:32 -05:00
Antonio SJ Musumeci 617f33dc36 branches2.hpp 2024-03-10 17:04:27 -05:00
Antonio SJ Musumeci 7241a8cc1a branches2.hpp 2024-03-10 17:04:07 -05:00
Antonio SJ Musumeci 6d13b4a1b2 branch_tier.hpp 2024-03-10 16:46:35 -05:00
Antonio SJ Musumeci 054739a67e branches2.cpp 2024-03-10 16:44:28 -05:00
Antonio SJ Musumeci a1929cd9ea mergerfs.cpp 2024-03-10 16:36:18 -05:00
Antonio SJ Musumeci d9067abb22 mergerfs.cpp 2024-03-10 16:36:07 -05:00
Antonio SJ Musumeci 8f6296813c mergerfs.cpp 2024-03-10 16:35:57 -05:00
Antonio SJ Musumeci 827eac2ae2 state.cpp 2024-03-10 16:20:13 -05:00
Antonio SJ Musumeci ab82ac4c08 mergerfs.cpp 2024-03-10 16:19:44 -05:00
Antonio SJ Musumeci 8c5ea30a8c branches2.hpp 2024-03-10 16:19:20 -05:00
Antonio SJ Musumeci 3fe9d52faf branches2.cpp 2024-03-10 16:19:07 -05:00
Antonio SJ Musumeci 1040653830 mergerfs.cpp 2024-03-10 16:18:53 -05:00
Antonio SJ Musumeci 828e0abca4 state.hpp 2024-03-10 16:17:57 -05:00
Antonio SJ Musumeci 28a3d614b3 state.hpp 2024-03-10 16:17:39 -05:00
Antonio SJ Musumeci c635bd4525 state.hpp 2024-03-10 16:16:58 -05:00
Antonio SJ Musumeci 37db0094ff state.hpp 2024-03-10 16:16:49 -05:00
Antonio SJ Musumeci 60a0ea9e44 branch_tier.hpp 2024-03-10 15:27:29 -05:00
Antonio SJ Musumeci b1d725032e branch_tier.cpp 2024-03-10 15:27:19 -05:00
Antonio SJ Musumeci 281c8eef63 branches2.cpp 2024-03-10 15:07:23 -05:00
Antonio SJ Musumeci f2f6efd9af branch_tier.cpp 2024-03-10 14:57:14 -05:00
Antonio SJ Musumeci 2c60026c06 branch_tier.cpp 2024-03-10 14:53:24 -05:00
Antonio SJ Musumeci faa7e4d5cb mergerfs.cpp 2024-03-10 14:51:31 -05:00
Antonio SJ Musumeci cf781ad44b mergerfs.cpp 2024-03-10 14:49:50 -05:00
Antonio SJ Musumeci 591daad2d4 mergerfs.cpp 2024-03-10 14:49:39 -05:00
Antonio SJ Musumeci 661d5668e9 branch_tier.cpp 2024-03-10 14:43:12 -05:00
Antonio SJ Musumeci 9b1d55840d branch_tier.hpp 2024-02-07 23:03:07 -06:00
Antonio SJ Musumeci dadbab5b77 branch_tier.cpp 2024-02-07 23:00:22 -06:00
Antonio SJ Musumeci 737a694adf branches2.cpp 2024-02-07 22:47:08 -06:00
Antonio SJ Musumeci 72fe28bafb branches2.cpp 2024-02-07 22:38:21 -06:00
Antonio SJ Musumeci b6b28aed41 branches2.cpp 2024-02-07 22:38:19 -06:00
Antonio SJ Musumeci 7d412d17c4 branches2.hpp 2024-02-07 22:35:30 -06:00
Antonio SJ Musumeci 045c83afe0 branches2.cpp 2024-02-07 22:25:57 -06:00
Antonio SJ Musumeci 928eef672b branches2.cpp 2024-02-07 22:25:31 -06:00
Antonio SJ Musumeci 9ae8b8095c branches2.cpp 2024-02-07 22:25:21 -06:00
Antonio SJ Musumeci 0c75e08b8a branches2.cpp 2024-02-07 21:06:21 -06:00
Antonio SJ Musumeci 969a0551d6 branches2.cpp 2024-02-07 21:06:03 -06:00
Antonio SJ Musumeci 7c546a8c43 branches2.hpp 2024-02-07 21:05:00 -06:00
Antonio SJ Musumeci 90952b9dcc branch2.cpp 2024-02-07 21:03:51 -06:00
Antonio SJ Musumeci 36cab88830 branch2.hpp 2024-02-07 21:03:36 -06:00
Antonio SJ Musumeci fedcd631f0 branch_tier.cpp 2024-02-07 21:03:03 -06:00
Antonio SJ Musumeci 7f0c4a19e6 branch_tier.cpp 2024-02-07 21:02:38 -06:00
Antonio SJ Musumeci e194749631 branch_tier.cpp 2024-02-07 21:02:16 -06:00
Antonio SJ Musumeci 95e3f1ffd1 branch_tier.hpp 2024-02-07 20:59:54 -06:00
Antonio SJ Musumeci d86ce57abb branch_tier.hpp 2024-02-07 20:59:48 -06:00
Antonio SJ Musumeci deb09d9ec4 branch2.cpp 2024-02-07 20:55:13 -06:00
Antonio SJ Musumeci 885a303f2a branch2.cpp 2024-02-07 20:54:55 -06:00
Antonio SJ Musumeci 55fb177b9c branch2.cpp 2024-02-07 20:50:59 -06:00
Antonio SJ Musumeci 375accd1a2 branch2.hpp 2024-02-07 20:48:03 -06:00
Antonio SJ Musumeci 58c42297ae mergerfs.cpp 2024-02-07 20:46:55 -06:00
Antonio SJ Musumeci 6b96de84a4 mergerfs.cpp 2024-02-07 20:46:25 -06:00
Antonio SJ Musumeci cd7fe42b7e branch_tier.cpp 2024-02-07 20:44:32 -06:00
Antonio SJ Musumeci 6cb0e66d74 branch_tier.hpp 2024-02-07 20:43:57 -06:00
Antonio SJ Musumeci 1a0a2d7ea3 branches2.cpp 2024-02-07 20:43:42 -06:00
Antonio SJ Musumeci d8b65cf64b branch_tier.cpp 2024-02-07 20:42:59 -06:00
Antonio SJ Musumeci 75692c066b branch2.cpp 2024-02-07 20:42:50 -06:00
Antonio SJ Musumeci df35fdbd19 branch2.cpp 2024-02-07 20:42:23 -06:00
Antonio SJ Musumeci d92b3fd5bf branch2.cpp 2024-02-07 20:39:54 -06:00
Antonio SJ Musumeci 6e708532b5 branch2.cpp 2024-02-07 20:39:43 -06:00
Antonio SJ Musumeci 5fc06d194f branch_tier.cpp 2024-02-07 20:38:40 -06:00
Antonio SJ Musumeci f2cda95640 branch2.cpp 2024-02-07 20:38:26 -06:00
Antonio SJ Musumeci 7002064853 branch2.cpp 2024-02-07 20:37:49 -06:00
Antonio SJ Musumeci b242e3fd3c branch_tier.cpp 2024-02-07 20:37:18 -06:00
Antonio SJ Musumeci 21dee5626d branch_tier.cpp 2024-02-07 20:37:05 -06:00
Antonio SJ Musumeci af1dc0bc06 branch_tier.cpp 2024-02-07 20:36:36 -06:00
Antonio SJ Musumeci 8ce21123a9 branch_tier.cpp 2024-02-07 20:35:13 -06:00
Antonio SJ Musumeci acaf872fef branch_tier.hpp 2024-02-07 20:34:33 -06:00
Antonio SJ Musumeci 2dcc27f5b4 branch2.cpp 2024-02-07 20:18:25 -06:00
Antonio SJ Musumeci 2d6fe4711e branch2.cpp 2024-02-07 20:18:23 -06:00
Antonio SJ Musumeci c8cb3b967c branch2.hpp 2024-02-07 20:17:16 -06:00
Antonio SJ Musumeci 374d629b07 func_create_ff.hpp 2024-02-07 20:07:26 -06:00
Antonio SJ Musumeci 536182e948 func_create_ff.hpp 2024-02-07 20:07:12 -06:00
Antonio SJ Musumeci 1543645eef func_create_ff.cpp 2024-02-07 20:06:35 -06:00
Antonio SJ Musumeci 7fc3dc27eb func_create_ff.cpp 2024-02-07 20:04:56 -06:00
Antonio SJ Musumeci e7531c193c func_create_ff.cpp 2024-02-07 20:03:46 -06:00
Antonio SJ Musumeci eb060cc9f9 fs_acl.cpp 2024-02-07 00:04:18 -06:00
Antonio SJ Musumeci 63362a20a9 fs_acl.cpp 2024-02-07 00:04:12 -06:00
Antonio SJ Musumeci daa7443eba fs_acl.cpp 2024-02-07 00:01:24 -06:00
Antonio SJ Musumeci 7323bcd7d3 fs_acl.cpp 2024-02-07 00:01:13 -06:00
Antonio SJ Musumeci 6c4f4b2aca fs_acl.cpp 2024-02-07 00:01:04 -06:00
Antonio SJ Musumeci c527c80397 fs_fgetxattr.hpp 2024-02-07 00:00:34 -06:00
Antonio SJ Musumeci 0a561c0e3a fs_fgetxattr.hpp 2024-02-07 00:00:08 -06:00
Antonio SJ Musumeci dcfc5e0f60 fs_openat.hpp 2024-02-06 23:59:22 -06:00
Antonio SJ Musumeci 1798adad65 fs_openat.hpp 2024-02-06 23:59:20 -06:00
Antonio SJ Musumeci 35ddb9d86d fs_acl.cpp 2024-02-06 23:57:59 -06:00
Antonio SJ Musumeci 3d50a30208 fs_acl.cpp 2024-02-06 23:57:44 -06:00
Antonio SJ Musumeci b1284db03b fs_acl.cpp 2024-02-06 23:57:33 -06:00
Antonio SJ Musumeci 88e4dc87cb fs_acl.cpp 2024-02-06 23:57:21 -06:00
Antonio SJ Musumeci 3502098c3c fs_acl.cpp 2024-02-06 23:56:20 -06:00
Antonio SJ Musumeci a10dddf24d fs_acl.cpp 2024-02-06 23:55:19 -06:00
Antonio SJ Musumeci cf807476f1 fs_acl.cpp 2024-02-06 23:45:55 -06:00
Antonio SJ Musumeci 20379e4dec fs_acl.cpp 2024-02-06 23:44:53 -06:00
Antonio SJ Musumeci e65946991c func_create_ff.cpp 2024-02-06 23:34:22 -06:00
Antonio SJ Musumeci acf1f3df06 fs_openat.hpp 2024-02-06 23:29:05 -06:00
Antonio SJ Musumeci 2221d5c4a5 fs_openat.hpp 2024-02-06 23:28:46 -06:00
Antonio SJ Musumeci 6af1ee31b3 func_create_ff.cpp 2024-02-06 23:28:07 -06:00
Antonio SJ Musumeci cfcd73ed4b fs_openat.hpp 2024-02-06 23:27:40 -06:00
Antonio SJ Musumeci ee6831e16a fs_openat.hpp 2024-02-06 23:26:41 -06:00
Antonio SJ Musumeci 287efc5923 fs_openat.hpp 2024-02-06 23:26:34 -06:00
Antonio SJ Musumeci af4301f889 func_create_ff.cpp 2024-02-06 23:25:52 -06:00
Antonio SJ Musumeci 5c6df900de checkpoint 2024-02-05 20:52:54 -06:00
Antonio SJ Musumeci 3676296b74 state.cpp 2024-02-05 15:27:40 -06:00
Antonio SJ Musumeci 79bbeaf37e state.cpp 2024-02-05 15:27:17 -06:00
Antonio SJ Musumeci 7689dbe9c3 state.cpp 2024-02-05 11:59:29 -06:00
Antonio SJ Musumeci d7d0be6b78 state.cpp 2024-02-05 11:59:04 -06:00
Antonio SJ Musumeci 7da74011fd state.cpp 2024-02-05 11:55:52 -06:00
Antonio SJ Musumeci c89b7d5dba func_create_ff.hpp 2024-02-05 11:54:47 -06:00
Antonio SJ Musumeci 939b5a9516 func_create_ff.hpp 2024-02-05 11:54:35 -06:00
Antonio SJ Musumeci 0112b4f78f func_create_ff.hpp 2024-02-05 11:54:24 -06:00
Antonio SJ Musumeci d3d1963778 func_create_ff.cpp 2024-02-05 11:54:02 -06:00
Antonio SJ Musumeci d5ecf78fc6 func_create_ff.cpp 2024-02-05 11:53:51 -06:00
Antonio SJ Musumeci e090ea15ca func_create_ff.cpp 2024-02-05 11:53:21 -06:00
Antonio SJ Musumeci 91e9b6f74a func_create_base.hpp 2024-02-05 11:51:43 -06:00
Antonio SJ Musumeci f9cd21280f state.hpp 2024-02-05 11:51:27 -06:00
Antonio SJ Musumeci 21c97ac6d6 mergerfs.cpp 2024-02-05 11:50:31 -06:00
Antonio SJ Musumeci cfa69245af func_create_base.hpp 2024-02-05 11:48:01 -06:00
Antonio SJ Musumeci 47be2da7a9 func_create_base.hpp 2024-02-05 11:47:58 -06:00
Antonio SJ Musumeci 50593e7646 func_create_base.hpp 2024-02-05 11:47:51 -06:00
Antonio SJ Musumeci c89227d7ef func_create_base.hpp 2024-02-05 11:47:41 -06:00
Antonio SJ Musumeci c093409071 state.hpp 2024-02-05 11:44:45 -06:00
Antonio SJ Musumeci 7472ed0db7 state.hpp 2024-02-05 11:44:37 -06:00
Antonio SJ Musumeci 4a5b0fb932 mergerfs.cpp 2024-02-05 07:46:37 -06:00
Antonio SJ Musumeci 8aa4136495 mergerfs.cpp 2024-02-05 07:45:55 -06:00
Antonio SJ Musumeci d8fbe9da00 state.cpp 2024-02-04 23:54:02 -06:00
Antonio SJ Musumeci 9bb3a43016 mergerfs.cpp 2024-02-04 23:53:52 -06:00
Antonio SJ Musumeci 75f2bf6436 mergerfs.cpp 2024-02-04 23:43:15 -06:00
Antonio SJ Musumeci 9003157532 mergerfs.cpp 2024-02-04 23:42:51 -06:00
Antonio SJ Musumeci 6407d4174a mergerfs.cpp 2024-02-04 23:42:44 -06:00
Antonio SJ Musumeci 6745e1d7fd state.hpp 2024-02-04 23:40:32 -06:00
Antonio SJ Musumeci ab8b2c2944 branch_tier.hpp 2024-02-04 22:46:23 -06:00
Antonio SJ Musumeci 7de2da705a func_create_ff.cpp 2024-02-04 22:44:15 -06:00
Antonio SJ Musumeci 2af12836db func_create_ff.cpp 2024-02-04 22:44:14 -06:00
Antonio SJ Musumeci 973463d3d4 func_create_ff.cpp 2024-02-04 22:44:08 -06:00
Antonio SJ Musumeci b45836cec4 func_create_ff.cpp 2024-02-04 22:43:56 -06:00
Antonio SJ Musumeci f7e91539ce func_create_ff.cpp 2024-02-04 22:42:27 -06:00
Antonio SJ Musumeci e7746075fa func_create_ff.cpp 2024-02-04 22:41:49 -06:00
Antonio SJ Musumeci 99115da9f8 branches2.hpp 2024-02-04 22:27:13 -06:00
Antonio SJ Musumeci 89a88d65f5 branches2.hpp 2024-02-04 22:27:12 -06:00
Antonio SJ Musumeci ccb7226b45 branch_tier.hpp 2024-02-04 22:26:45 -06:00
Antonio SJ Musumeci 735c5df61c branch_tier.hpp 2024-02-04 22:26:44 -06:00
Antonio SJ Musumeci c5d7a4d4e6 branch_tier.hpp 2024-02-04 22:26:25 -06:00
Antonio SJ Musumeci a736e6ad65 branch_tier.hpp 2024-02-04 22:26:11 -06:00
Antonio SJ Musumeci 625a5bf13a branch_tier.hpp 2024-02-04 22:25:50 -06:00
Antonio SJ Musumeci 11dd3d376c branch_tier.hpp 2024-02-04 22:25:44 -06:00
Antonio SJ Musumeci 5b5d9c6823 branch_tier.hpp 2024-02-04 22:25:24 -06:00
Antonio SJ Musumeci ed38926dd4 branches2.cpp 2024-02-04 22:24:35 -06:00
Antonio SJ Musumeci 05140a515d branches2.cpp 2024-02-04 22:24:23 -06:00
Antonio SJ Musumeci 962ee01ccc branches2.cpp 2024-02-04 22:24:05 -06:00
Antonio SJ Musumeci ad981dedbe branches2.cpp 2024-02-04 22:23:52 -06:00
Antonio SJ Musumeci b22991b878 branches2.cpp 2024-02-04 22:23:35 -06:00
Antonio SJ Musumeci 2990ea3dd3 branches2.cpp 2024-02-04 22:20:26 -06:00
Antonio SJ Musumeci 6ea01ce6cb func_create_ff.cpp 2024-02-04 22:19:21 -06:00
Antonio SJ Musumeci 0774593370 func_create_ff.cpp 2024-02-04 22:18:50 -06:00
Antonio SJ Musumeci 3869237b25 branches2.hpp 2024-02-04 22:18:14 -06:00
Antonio SJ Musumeci 2e012e811c func_create_ff.cpp 2024-02-04 22:17:37 -06:00
Antonio SJ Musumeci 5813559c81 func_create_ff.cpp 2024-02-04 22:16:41 -06:00
Antonio SJ Musumeci b9c5ff676c func_create_ff.cpp 2024-02-04 22:16:26 -06:00
Antonio SJ Musumeci df4fe1beb1 func_create_ff.hpp 2024-02-04 22:15:36 -06:00
Antonio SJ Musumeci 079f1149e9 func_create_base.hpp 2024-02-04 22:15:12 -06:00
Antonio SJ Musumeci 6db1e2ecd6 func_create_base.hpp 2024-02-04 22:15:07 -06:00
Antonio SJ Musumeci 0d229f0676 func_create_ff.cpp 2024-02-04 22:14:24 -06:00
Antonio SJ Musumeci 2ef7be823d branches2.hpp 2024-02-04 22:13:52 -06:00
Antonio SJ Musumeci fe07e69ced func_create_ff.cpp 2024-02-04 22:12:32 -06:00
Antonio SJ Musumeci 17b471ba8b func_create_ff.cpp 2024-02-04 22:12:20 -06:00
Antonio SJ Musumeci 789821bbac func_create_ff.cpp 2024-02-04 21:47:10 -06:00
Antonio SJ Musumeci c9dc8ca49b func_create_ff.cpp 2024-02-04 21:47:09 -06:00
Antonio SJ Musumeci 26a172295e func_create_ff.cpp 2024-02-04 21:45:54 -06:00
Antonio SJ Musumeci 35d2e490f3 func_create_ff.cpp 2024-02-04 21:45:34 -06:00
Antonio SJ Musumeci 660ecc9259 func_create_ff.cpp 2024-02-04 21:42:31 -06:00
Antonio SJ Musumeci bbbbd50fc7 func_create_ff.cpp 2024-02-04 21:42:10 -06:00
Antonio SJ Musumeci bbb52db9af func_create_ff.cpp 2024-02-04 21:41:38 -06:00
Antonio SJ Musumeci bbf4b6e52a func_create_ff.cpp 2024-02-04 21:41:34 -06:00
Antonio SJ Musumeci e708b8db26 func_create_ff.cpp 2024-02-04 21:41:12 -06:00
Antonio SJ Musumeci 4bf68b8860 func_create_ff.cpp 2024-02-04 21:40:04 -06:00
Antonio SJ Musumeci 84325f8c94 func_create_ff.cpp 2024-02-04 21:39:17 -06:00
Antonio SJ Musumeci 3c9c746b92 func_create_ff.cpp 2024-02-04 21:38:59 -06:00
Antonio SJ Musumeci 7bc3600ff9 checkpoint 2024-02-04 19:52:51 -06:00
Antonio SJ Musumeci 5b21a5df54 fuse_create.cpp 2024-02-04 19:52:30 -06:00
Antonio SJ Musumeci d0146fb88f fuse_create.cpp 2024-02-04 19:52:22 -06:00
Antonio SJ Musumeci 7dd4df76f1 fuse_create.cpp 2024-02-04 19:51:54 -06:00
Antonio SJ Musumeci f8010dd9ce fuse_create.cpp 2024-02-04 19:51:02 -06:00
Antonio SJ Musumeci cb7a088dd7 fuse_create.cpp 2024-02-04 19:48:32 -06:00
Antonio SJ Musumeci 786b8eb1f8 func_create_ff.cpp 2024-02-04 13:51:48 -06:00
Antonio SJ Musumeci a9be0bb0d9 func_create_ff.cpp 2024-02-04 13:27:47 -06:00
Antonio SJ Musumeci d7eeb05a29 func_create_ff.cpp 2024-02-04 13:27:40 -06:00
Antonio SJ Musumeci 720d75b9bd checkpoint 2024-02-04 01:19:44 -06:00
Antonio SJ Musumeci df393cb1ed state.cpp 2024-02-04 00:31:00 -06:00
Antonio SJ Musumeci 95dea4020f func_create_ff.cpp 2024-02-04 00:29:55 -06:00
Antonio SJ Musumeci eb93ef6191 state.cpp 2024-02-04 00:28:14 -06:00
Antonio SJ Musumeci 9869198db3 state.cpp 2024-02-04 00:27:59 -06:00
Antonio SJ Musumeci cd6d77e632 state.hpp 2024-02-04 00:21:31 -06:00
Antonio SJ Musumeci eaa3a40a40 state.hpp 2024-02-04 00:21:09 -06:00
Antonio SJ Musumeci e8d6758de3 state.hpp 2024-02-04 00:20:43 -06:00
Antonio SJ Musumeci c5f4794591 func_create_base.hpp 2024-02-03 23:28:05 -06:00
Antonio SJ Musumeci 34bea0ebcf func_create_base.hpp 2024-02-03 23:27:32 -06:00
Antonio SJ Musumeci 922499415f func_create_base.hpp 2024-02-03 23:27:10 -06:00
Antonio SJ Musumeci 0c2f684f2a func_create_base.hpp 2024-02-03 23:26:15 -06:00
Antonio SJ Musumeci 94a43849f5 func_create_base.hpp 2024-02-03 23:25:57 -06:00
Antonio SJ Musumeci 311bdd4642 func_create_base.hpp 2024-02-03 23:25:27 -06:00
Antonio SJ Musumeci 6daab1534a func_create_ff.hpp 2024-02-03 23:24:56 -06:00
Antonio SJ Musumeci 2492d50e1e func_create_ff.hpp 2024-02-03 23:22:15 -06:00
Antonio SJ Musumeci c65a030ed3 func_create_ff.hpp 2024-02-03 23:22:02 -06:00
Antonio SJ Musumeci afff02bdf1 func_create_ff.hpp 2024-02-03 23:19:50 -06:00
Antonio SJ Musumeci 958a2ef980 func_create_ff.hpp 2024-02-03 23:19:45 -06:00
Antonio SJ Musumeci 7ba9bab4c0 func_create_ff.hpp 2024-02-03 23:19:44 -06:00
Antonio SJ Musumeci 60d145090a branches2.hpp 2024-02-03 23:19:18 -06:00
Antonio SJ Musumeci af82886d5d branches2.hpp 2024-02-03 23:19:13 -06:00
Antonio SJ Musumeci 770e069d10 fuse_create_base.hpp 2024-02-03 23:17:20 -06:00
Antonio SJ Musumeci 233fb5b6e5 fuse_create_base.hpp 2024-02-03 23:14:15 -06:00
Antonio SJ Musumeci c02abf198f fuse_create_base.hpp 2024-02-03 23:14:10 -06:00
Antonio SJ Musumeci 200a043bf7 fuse_create_base.hpp 2024-02-03 23:13:56 -06:00
Antonio SJ Musumeci 2c437b6143 fuse_create_base.hpp 2024-02-03 23:06:03 -06:00
Antonio SJ Musumeci 58d1b95e4d fuse_create_base.hpp 2024-02-03 23:05:43 -06:00
Antonio SJ Musumeci 055f8c53d4 fuse_create_base.hpp 2024-02-03 20:19:15 -06:00
Antonio SJ Musumeci 0522f317ed fuse_create_base.hpp 2024-02-03 20:18:59 -06:00
Antonio SJ Musumeci 7ca0e18c30 fuse_create_base.hpp 2024-02-03 20:18:58 -06:00
Antonio SJ Musumeci 511bde04fd fuse_create_base.hpp 2024-02-03 20:17:12 -06:00
Antonio SJ Musumeci 638cf9b2a9 checkpoint 2024-02-03 20:16:55 -06:00
Antonio SJ Musumeci afc774d3d5 branch2.hpp 2024-02-03 19:56:44 -06:00
Antonio SJ Musumeci 0a9bba712c branch2.hpp 2024-02-03 19:56:23 -06:00
Antonio SJ Musumeci e06e5422c1 mergerfs.cpp 2024-02-03 19:52:03 -06:00
Antonio SJ Musumeci 3b910616ce mergerfs.cpp 2024-02-03 19:51:00 -06:00
Antonio SJ Musumeci 74a9062912 mergerfs.cpp 2024-02-03 19:50:08 -06:00
Antonio SJ Musumeci 5772342f7a branch_tier.hpp 2024-02-03 19:49:48 -06:00
Antonio SJ Musumeci 96a5fc9f0c branches2.hpp 2024-02-03 19:48:26 -06:00
Antonio SJ Musumeci 6e27f8e50b mergerfs.cpp 2024-02-03 19:47:23 -06:00
Antonio SJ Musumeci 594655afcd mergerfs.cpp 2024-02-03 19:43:23 -06:00
Antonio SJ Musumeci ed18196540 mergerfs.cpp 2024-02-03 18:49:10 -06:00
Antonio SJ Musumeci c3de4da174 mergerfs.cpp 2024-02-03 18:49:02 -06:00
Antonio SJ Musumeci c3dde12553 mergerfs.cpp 2024-02-03 18:48:34 -06:00
Antonio SJ Musumeci c91685be4d checkpoint 2024-02-02 00:00:30 -06:00
Antonio SJ Musumeci 63928dd555 branch2.cpp 2024-02-01 23:58:55 -06:00
Antonio SJ Musumeci 7a4cf5c139 branch2.cpp 2024-02-01 23:53:46 -06:00
Antonio SJ Musumeci 77b9965843 branch_tier.cpp 2024-02-01 23:31:27 -06:00
Antonio SJ Musumeci 1d2672314b branch_tier.cpp 2024-02-01 23:29:40 -06:00
Antonio SJ Musumeci 16155c2431 branch_tier.cpp 2024-02-01 23:28:13 -06:00
Antonio SJ Musumeci d516a7fb6f branch_tier.cpp 2024-02-01 23:27:46 -06:00
Antonio SJ Musumeci 141486564e branch_tier.cpp 2024-02-01 23:27:42 -06:00
Antonio SJ Musumeci 2781cc1bb4 branch_tier.cpp 2024-02-01 23:27:18 -06:00
Antonio SJ Musumeci 773e611c1e branch_tier.cpp 2024-02-01 23:27:08 -06:00
Antonio SJ Musumeci 7ddbc50fd8 branch_tier.cpp 2024-02-01 23:26:31 -06:00
Antonio SJ Musumeci 5adf732c61 branch_tier.cpp 2024-02-01 23:22:58 -06:00
Antonio SJ Musumeci a3fdbaebf4 branch_tier.cpp 2024-02-01 23:22:50 -06:00
Antonio SJ Musumeci d7b623b86c branch_tier.cpp 2024-02-01 23:22:43 -06:00
Antonio SJ Musumeci 793a5043ad branch_tier.cpp 2024-02-01 23:21:59 -06:00
Antonio SJ Musumeci fd28af64d3 checkpoint 2024-02-01 23:17:52 -06:00
Antonio SJ Musumeci 2e20362d76 branch_tier.cpp 2024-02-01 23:16:18 -06:00
Antonio SJ Musumeci 1a53c0bffb branch_tier.cpp 2024-02-01 23:16:13 -06:00
Antonio SJ Musumeci 8c2ab6f4d2 branch_tier.cpp 2024-02-01 23:15:02 -06:00
Antonio SJ Musumeci 09bb940204 branch2.cpp 2024-01-31 23:44:32 -06:00
Antonio SJ Musumeci c87832c10a branch2.cpp 2024-01-31 23:43:39 -06:00
Antonio SJ Musumeci 0d49296c2a branch2.hpp 2024-01-31 23:42:51 -06:00
Antonio SJ Musumeci e4c3f6dafc branch2.cpp 2024-01-31 23:42:43 -06:00
Antonio SJ Musumeci 3d450fa82a branch2.hpp 2024-01-31 23:31:35 -06:00
Antonio SJ Musumeci 7f29e03fc5 branch2.cpp 2024-01-31 23:31:16 -06:00
Antonio SJ Musumeci dc5d544f1b branch_tier.cpp 2024-01-31 23:30:21 -06:00
Antonio SJ Musumeci e7735c1f63 branch_tier.cpp 2024-01-31 23:30:19 -06:00
Antonio SJ Musumeci 3b20b9b7ba branch2.cpp 2024-01-31 23:29:42 -06:00
Antonio SJ Musumeci b1fb9de8cf branch2.cpp 2024-01-31 23:29:23 -06:00
Antonio SJ Musumeci 47ff4738f9 branch2.cpp 2024-01-31 23:28:19 -06:00
Antonio SJ Musumeci 9646f148fa branch2.cpp 2024-01-31 23:27:07 -06:00
Antonio SJ Musumeci 160a4de0f3 branch2.cpp 2024-01-31 23:26:18 -06:00
Antonio SJ Musumeci e73cb07277 branch2.cpp 2024-01-31 23:26:15 -06:00
Antonio SJ Musumeci db23c0a3a0 branch2.cpp 2024-01-31 23:25:24 -06:00
Antonio SJ Musumeci 42b6190c03 branch2.hpp 2024-01-31 23:23:36 -06:00
Antonio SJ Musumeci 90d9d8f0d5 branch_tier.cpp 2024-01-31 23:20:22 -06:00
Antonio SJ Musumeci 3c0d47df93 branch_tier.cpp 2024-01-31 23:20:06 -06:00
Antonio SJ Musumeci f6aa1c71ce branch_tier.cpp 2024-01-31 23:18:55 -06:00
Antonio SJ Musumeci 305013a702 branch_tier.cpp 2024-01-31 23:18:51 -06:00
Antonio SJ Musumeci 28123ac38b branch_tier.cpp 2024-01-31 23:18:43 -06:00
Antonio SJ Musumeci 8843afc96b branch_tier.cpp 2024-01-31 23:17:35 -06:00
Antonio SJ Musumeci 652575ccf2 branch_tier.cpp 2024-01-31 23:14:55 -06:00
Antonio SJ Musumeci b85a9c82f9 branch_tier.cpp 2024-01-31 23:14:48 -06:00
Antonio SJ Musumeci 54c019814f branch_tier.cpp 2024-01-31 23:02:55 -06:00
Antonio SJ Musumeci 2c51bd22ef branch_tier.cpp 2024-01-31 23:00:22 -06:00
Antonio SJ Musumeci 435b0ef395 branch2.cpp 2024-01-31 22:59:56 -06:00
Antonio SJ Musumeci 6f410a1aa9 branch2.cpp 2024-01-31 22:59:26 -06:00
Antonio SJ Musumeci d48be5882e mergerfs.cpp 2024-01-31 22:45:41 -06:00
Antonio SJ Musumeci 409e0ae49f mergerfs.cpp 2024-01-31 22:45:40 -06:00
Antonio SJ Musumeci 53ad53337a mergerfs.cpp 2024-01-31 22:44:53 -06:00
Antonio SJ Musumeci 356be5524c branch2.cpp 2024-01-31 22:44:36 -06:00
Antonio SJ Musumeci e575a8d1e2 branch_tier.cpp 2024-01-31 22:41:01 -06:00
Antonio SJ Musumeci cf21b64b58 branch_tier.cpp 2024-01-31 22:40:44 -06:00
Antonio SJ Musumeci fa1d57cdf3 branch_tier.cpp 2024-01-31 22:35:59 -06:00
Antonio SJ Musumeci 0b882c75a6 branch_tier.cpp 2024-01-31 22:19:35 -06:00
Antonio SJ Musumeci ff568417f0 branch_tier.cpp 2024-01-31 22:18:41 -06:00
Antonio SJ Musumeci d18a2ce30a branch_tier.cpp 2024-01-31 22:18:40 -06:00
Antonio SJ Musumeci 57ad56338a branch_tier.cpp 2024-01-31 22:18:26 -06:00
Antonio SJ Musumeci c5dacc4235 branch_tier.cpp 2024-01-31 22:17:30 -06:00
Antonio SJ Musumeci 88c89af88a branch2.cpp 2024-01-31 22:04:20 -06:00
Antonio SJ Musumeci 5ec9bf26a2 branch2.cpp 2024-01-31 21:57:36 -06:00
Antonio SJ Musumeci 9f06a7de91 branch2.hpp 2024-01-31 21:44:30 -06:00
Antonio SJ Musumeci e881f0ac0e branch2.cpp 2024-01-31 21:43:46 -06:00
Antonio SJ Musumeci 82c16f5eb1 branch2.cpp 2024-01-31 21:43:32 -06:00
Antonio SJ Musumeci 2f78a3c1b0 branch2.cpp 2024-01-31 21:43:19 -06:00
Antonio SJ Musumeci eb5d11d74c branch2.cpp 2024-01-31 21:38:38 -06:00
Antonio SJ Musumeci 7129122fc3 branch2.cpp 2024-01-31 21:38:19 -06:00
Antonio SJ Musumeci 1b57f55403 branch2.cpp 2024-01-31 21:38:11 -06:00
Antonio SJ Musumeci d27ef7bc51 branch_tier.cpp 2024-01-31 21:21:12 -06:00
Antonio SJ Musumeci 5aa07d3956 branch2.cpp 2024-01-31 21:20:34 -06:00
Antonio SJ Musumeci 497f7424f7 branch2.cpp 2024-01-31 21:19:57 -06:00
Antonio SJ Musumeci c28e30528f branch2.cpp 2024-01-31 21:19:29 -06:00
Antonio SJ Musumeci 90f41c7075 branch2.cpp 2024-01-31 21:19:28 -06:00
Antonio SJ Musumeci 18635d489a branch2.cpp 2024-01-31 21:17:45 -06:00
Antonio SJ Musumeci 66098421d8 branch2.cpp 2024-01-31 21:16:44 -06:00
Antonio SJ Musumeci 3ae5367cb4 branch_tier.cpp 2024-01-31 21:04:14 -06:00
Antonio SJ Musumeci 3820c3364b branch2.cpp 2024-01-29 00:07:45 -06:00
Antonio SJ Musumeci 03d90a3899 branch2.cpp 2024-01-29 00:07:09 -06:00
Antonio SJ Musumeci 7d5099a3fd branch2.cpp 2024-01-29 00:06:00 -06:00
Antonio SJ Musumeci 21479f0874 branch2.cpp 2024-01-29 00:05:52 -06:00
Antonio SJ Musumeci c72b54a161 branch2.cpp 2024-01-29 00:05:28 -06:00
Antonio SJ Musumeci 2be4f0b1dd branch2.cpp 2024-01-29 00:04:57 -06:00
Antonio SJ Musumeci 87085b281d branch2.cpp 2024-01-29 00:03:46 -06:00
Antonio SJ Musumeci 0a4a9ec7ce branch2.cpp 2024-01-29 00:02:21 -06:00
Antonio SJ Musumeci e4717dcbe6 branch2.cpp 2024-01-29 00:02:15 -06:00
Antonio SJ Musumeci 35b573c5fa branch2.cpp 2024-01-29 00:02:03 -06:00
Antonio SJ Musumeci e85ca0d23d branch2.cpp 2024-01-29 00:01:30 -06:00
Antonio SJ Musumeci 8d891c51c5 branch2.cpp 2024-01-29 00:00:59 -06:00
Antonio SJ Musumeci 0cfa5e39af branch2.hpp 2024-01-29 00:00:46 -06:00
Antonio SJ Musumeci ebe35f5a0b branch2.cpp 2024-01-29 00:00:38 -06:00
Antonio SJ Musumeci 0a7cc3008f branch2.hpp 2024-01-28 23:59:59 -06:00
Antonio SJ Musumeci c29fde6885 branch2.hpp 2024-01-28 23:58:24 -06:00
Antonio SJ Musumeci 7103327bdf branch2.hpp 2024-01-28 23:58:17 -06:00
Antonio SJ Musumeci 13bd184dda branch2.hpp 2024-01-28 23:57:45 -06:00
Antonio SJ Musumeci 4420dee26a branch2.hpp 2024-01-28 23:57:35 -06:00
Antonio SJ Musumeci 273e69502e branch2.hpp 2024-01-28 23:57:26 -06:00
Antonio SJ Musumeci 9e46159cac branch2.hpp 2024-01-28 23:57:06 -06:00
Antonio SJ Musumeci aab1f8e850 branch2.hpp 2024-01-28 23:56:18 -06:00
Antonio SJ Musumeci 4e03b2b287 branch2.hpp 2024-01-28 23:55:50 -06:00
Antonio SJ Musumeci ed95ce5442 branch2.hpp 2024-01-28 23:55:36 -06:00
Antonio SJ Musumeci 82b94c955c branch_tier.cpp 2024-01-28 23:31:31 -06:00
Antonio SJ Musumeci 1ad509a18c branch_tier.cpp 2024-01-28 23:31:30 -06:00
Antonio SJ Musumeci 8582fe5e5b branch_tier.cpp 2024-01-28 23:31:07 -06:00
Antonio SJ Musumeci 450fdaa0dd branch_tier.cpp 2024-01-28 23:29:59 -06:00
Antonio SJ Musumeci 8d8bbfc27f branch_tier.cpp 2024-01-28 23:29:04 -06:00
Antonio SJ Musumeci f15705ccad branch_tier.cpp 2024-01-28 23:24:13 -06:00
Antonio SJ Musumeci 7f188be486 branch_tier.cpp 2024-01-28 23:24:03 -06:00
Antonio SJ Musumeci 1350e9d1db branch_tier.cpp 2024-01-28 23:23:50 -06:00
Antonio SJ Musumeci 10d8e7d43f branch_tier.cpp 2024-01-28 23:19:24 -06:00
Antonio SJ Musumeci 9d5c9edd1d branch_tier.cpp 2024-01-28 23:03:45 -06:00
Antonio SJ Musumeci 40158677dc branch_tier.cpp 2024-01-28 23:03:34 -06:00
Antonio SJ Musumeci 479cfafe5a checkpoint 2024-01-28 22:45:38 -06:00
Antonio SJ Musumeci e769d94e00 branches2.cpp 2024-01-28 22:44:03 -06:00
Antonio SJ Musumeci 595fd8d538 branches2.hpp 2024-01-28 22:38:53 -06:00
Antonio SJ Musumeci 4d796051f5 branch2.hpp 2024-01-28 22:38:16 -06:00
Antonio SJ Musumeci be856a0cd8 branches2.hpp 2024-01-28 22:35:48 -06:00
Antonio SJ Musumeci 3724318306 branches2.hpp 2024-01-28 22:32:04 -06:00
Antonio SJ Musumeci d80773ceb8 branch_tier.hpp 2024-01-28 22:30:59 -06:00
Antonio SJ Musumeci 8bdc1fb2d2 branches2.cpp 2024-01-28 21:35:32 -06:00
Antonio SJ Musumeci 2a09c57615 branches2.cpp 2024-01-28 21:35:03 -06:00
Antonio SJ Musumeci 9b92ec5a37 branches2.cpp 2024-01-28 21:34:09 -06:00
Antonio SJ Musumeci ef74d52007 branches2.cpp 2024-01-28 21:33:53 -06:00
Antonio SJ Musumeci 1af5ca5945 branches2.cpp 2024-01-28 21:33:39 -06:00
Antonio SJ Musumeci 42b7d581f3 branches2.cpp 2024-01-28 21:33:28 -06:00
Antonio SJ Musumeci 46707ab24e branches2.cpp 2024-01-28 21:33:10 -06:00
Antonio SJ Musumeci 8d9ed5305b branches2.cpp 2024-01-28 21:32:55 -06:00
Antonio SJ Musumeci d63ed52a01 branches2.cpp 2024-01-27 01:00:38 -06:00
Antonio SJ Musumeci c4433efe5a branches2.cpp 2024-01-27 01:00:27 -06:00
Antonio SJ Musumeci 7528efa28b branches2.cpp 2024-01-27 01:00:17 -06:00
Antonio SJ Musumeci 011716d471 branches2.cpp 2024-01-27 00:58:36 -06:00
Antonio SJ Musumeci d89a944031 branches2.cpp 2024-01-27 00:57:46 -06:00
Antonio SJ Musumeci 0c4d9cc69f branches2.cpp 2024-01-27 00:57:34 -06:00
Antonio SJ Musumeci 20a2f83a33 branches2.cpp 2024-01-27 00:56:15 -06:00
Antonio SJ Musumeci b78d01a629 branches2.cpp 2024-01-27 00:56:08 -06:00
Antonio SJ Musumeci b7bb1ce0a1 branches2.cpp 2024-01-27 00:53:15 -06:00
Antonio SJ Musumeci ed92f3c67d branches2.cpp 2024-01-27 00:52:50 -06:00
Antonio SJ Musumeci cdf57f13ab branches2.cpp 2024-01-27 00:52:27 -06:00
Antonio SJ Musumeci bfec58843a branches2.cpp 2024-01-27 00:51:41 -06:00
Antonio SJ Musumeci 61d84a9418 branches2.cpp 2024-01-27 00:49:48 -06:00
Antonio SJ Musumeci 7b76221d1b branches2.cpp 2024-01-27 00:49:38 -06:00
Antonio SJ Musumeci d5b411ce44 mergerfs.cpp 2024-01-27 00:48:14 -06:00
Antonio SJ Musumeci 3a936dc743 mergerfs.cpp 2024-01-27 00:45:37 -06:00
Antonio SJ Musumeci b2e489e0c1 mergerfs.cpp 2024-01-27 00:45:30 -06:00
Antonio SJ Musumeci 9187a4b899 mergerfs.cpp 2024-01-27 00:45:00 -06:00
Antonio SJ Musumeci 39b72434ea mergerfs.cpp 2024-01-27 00:32:49 -06:00
Antonio SJ Musumeci 8fac887706 checkpoint 2024-01-26 00:41:09 -06:00
Antonio SJ Musumeci 0ab7ebf81c mergerfs.cpp 2024-01-26 00:30:04 -06:00
Antonio SJ Musumeci d0501b36c1 mergerfs.cpp 2024-01-25 23:47:25 -06:00
Antonio SJ Musumeci 20df3f3331 branches2.cpp 2024-01-25 23:46:12 -06:00
Antonio SJ Musumeci e7bc60351c branches2.hpp 2024-01-25 23:28:11 -06:00
Antonio SJ Musumeci fde366c932 branches2.hpp 2024-01-25 23:28:03 -06:00
Antonio SJ Musumeci 3c8b39fe6b branch2.cpp 2024-01-25 23:04:07 -06:00
Antonio SJ Musumeci 3ff5e50a36 branch2.hpp 2024-01-25 23:04:07 -06:00
Antonio SJ Musumeci 92a38fd39e branch2.hpp 2024-01-25 23:04:07 -06:00
Antonio SJ Musumeci 4b099d1d3b branch2.cpp 2024-01-25 23:04:07 -06:00
Antonio SJ Musumeci f7e91d9c8e branch2.cpp 2024-01-25 23:04:07 -06:00
Antonio SJ Musumeci 915ffaf6e9 branch2.hpp 2024-01-25 23:04:04 -06:00
Antonio SJ Musumeci 15f8ba594b mergerfs.cpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci efeb9fa8e3 mergerfs.cpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci cd7aab90a5 mergerfs.cpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci 87e043e7c4 branches2.hpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci 874e288386 state.cpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci 3198258f4a state.cpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci 86d5fe31fa state.hpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci afc51bec03 branches2.hpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci ec0c25c2b7 branches2.hpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci 631cd5fc16 branches2.hpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci c327662bd3 state.cpp 2024-01-25 23:03:32 -06:00
Antonio SJ Musumeci 0b145687ef checkpoint 2024-01-25 23:02:31 -06:00
Antonio SJ Musumeci 5c5e803c3d branches2.hpp 2024-01-25 07:55:39 -06:00
Antonio SJ Musumeci c5d7cbcf40 branches2.hpp 2024-01-25 07:55:14 -06:00
Antonio SJ Musumeci 3a831bf98b branches2.hpp 2024-01-25 07:55:02 -06:00
Antonio SJ Musumeci 2c337dc007 branches2.hpp 2024-01-25 07:36:06 -06:00
Antonio SJ Musumeci 693444b8ca branches2.hpp 2024-01-25 07:35:43 -06:00
Antonio SJ Musumeci bf75615b38 branches2.hpp 2024-01-24 23:42:14 -06:00
Antonio SJ Musumeci 936202d0bb branches2.hpp 2024-01-24 23:41:43 -06:00
Antonio SJ Musumeci 92aca75fab branches2.hpp 2024-01-24 23:38:33 -06:00
Antonio SJ Musumeci a024a45cd7 branches2.hpp 2024-01-24 23:33:52 -06:00
Antonio SJ Musumeci 3c34463ecb branches2.hpp 2024-01-24 23:26:48 -06:00
Antonio SJ Musumeci 43af15e54a branches2.hpp 2024-01-24 23:21:02 -06:00
Antonio SJ Musumeci c2babda0f7 branches2.hpp 2024-01-24 23:17:50 -06:00
Antonio SJ Musumeci 20fe9fc4d4 branches2.hpp 2024-01-24 23:00:45 -06:00
Antonio SJ Musumeci e0a0c41932 branches2.hpp 2024-01-24 23:00:43 -06:00
Antonio SJ Musumeci 3a1aebb875 branch2.hpp 2024-01-24 22:06:39 -06:00
Antonio SJ Musumeci 3f4daac1a1 branch2.hpp 2024-01-24 22:04:13 -06:00
Antonio SJ Musumeci 95b5cb83ef branch2.hpp 2024-01-24 21:53:35 -06:00
Antonio SJ Musumeci 4e7c815a25 branch2.hpp 2024-01-24 21:53:17 -06:00
Antonio SJ Musumeci b3088ec7c9 branch2.hpp 2024-01-24 21:52:51 -06:00
Antonio SJ Musumeci 8e0979a515 branch2.hpp 2024-01-24 21:49:25 -06:00
Antonio SJ Musumeci 1e746c3beb branch2.hpp 2024-01-24 21:49:02 -06:00
Antonio SJ Musumeci 0ef8b3a2f2 branch2.cpp 2024-01-24 21:48:36 -06:00
Antonio SJ Musumeci 6e95e1da01 branch2.hpp 2024-01-24 21:44:24 -06:00
Antonio SJ Musumeci 2df4ed4c6e checkpoint 2024-01-24 13:26:57 -06:00
Antonio SJ Musumeci 5deff9025a mergerfs.cpp 2024-01-23 21:20:01 -06:00
Antonio SJ Musumeci 4dcf8dbc56 mergerfs.cpp 2024-01-23 21:19:38 -06:00
Antonio SJ Musumeci 39edcc3371 mergerfs.cpp 2024-01-23 21:18:25 -06:00
Antonio SJ Musumeci 97b23e03da mergerfs.cpp 2024-01-23 21:18:09 -06:00
Antonio SJ Musumeci 1888e12616 checkpoint 2024-01-23 21:00:59 -06:00
Antonio SJ Musumeci e477978005 checkpoint 2024-01-23 20:04:32 -06:00
Antonio SJ Musumeci b3cb68d389 checkpoint 2024-01-23 20:03:42 -06:00
Antonio SJ Musumeci 120ac8b5da checkpoint 2024-01-23 20:02:49 -06:00
Antonio SJ Musumeci feb0fd4389 checkpoint 2024-01-23 20:01:25 -06:00
49 changed files with 17143 additions and 105 deletions
+2
View File
@@ -9,3 +9,5 @@
* fmt: https://github.com/fmtlib/fmt
* concurrentqueue: https://github.com/cameron314/concurrentqueue
* scope_guard: https://github.com/Neargye/scope_guard
* toml11: https://github.com/ToruNiina/toml11
* tsl ordered_map: https://github.com/Tessil/ordered-map
+129
View File
@@ -0,0 +1,129 @@
# test file
version = 0
[filesystem]
name = 'foo'
mountpoint = '/tmp/test'
threads = 0
[fuse]
posix-acl = false
async-read = true
message-size = 256
[func]
inode-calc = 'hybrid-hash'
symlinkify = false
symlinkify-timeout = 0
xattr = 'passthrough'
[func.access]
policy = 'ff'
[func.create]
policy = 'ff'
[func.getattr]
policy = 'ff'
follow-symlinks = 'never'
[func.rmdir]
policy = 'all'
follow-symlinks = 'never'
[func.link]
policy = 'all' # 'preserve-paths' | 'create-paths' | 'per-branch'
exdev = 'passthrough'
[func.write]
policy = 'null'
move-on-enospc = 'mfs'
[func.release]
drop-cache = true
[func.read]
policy = 'null'
[func.getxattr]
security-capability = false
[func.setxattr]
security-capability = false
[func.open]
link-cow = true
nfs-hack = 'off'
[func.rename]
policy = 'all'
exdev = 'passthrough'
[func.statfs]
policy = 'base'
ignore = 'none'
[func.readdir]
policy = 'posix'
readdirplus = false
[cache]
files = 'off'
statfs = 0
attr-timeout = 0
entry-timeout = 0
negative-entry-timeout = 0
writeback = false
symlinks = false
readdir = false
[branches]
min-free-space = 123
# TIER 0
[[branches.tier]]
enabled = true
[branches.tier.func.create]
policy.default = 'mfs'
[[branches.tier.func.create.policy.overrides]]
dir = 'foo/bar'
policy = 'lus'
[[branches.tier.func.create.policy.overrides]]
dir = 'blah'
policy = 'xyz'
[branches.tier.func.mkdir]
policy.default = 'mfs'
[[branches.tier.branch]]
enabled = true
name = 'branch0'
type = 'glob'
path = '*'
order = 'name:asc' # | size
mode = 'RW'
[[branches.tier.branch]]
enabled = true
name = 'branch1'
type = 'literal'
path = '/mnt/branch1'
mode = 'RW'
exclude = ['*.png']
[[branches.tier.branch]]
enabled = true
path = '/mnt/*'
type = 'glob'
mode = 'RW'
if-not-mountpoint = 'fail' # 'fail' | 'deactivate' ?
# TIER 1
[[branches.tier]]
[[branches.tier.branch]]
enabled = false
name = 'branch0'
type = 'literal'
path = '/mnt/*'
mode = 'RO'
+86
View File
@@ -0,0 +1,86 @@
#include "branch2.hpp"
#include <iostream>
#include <fcntl.h>
#include <fnmatch.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
Branch2::Branch2()
: enabled(false),
mode(Mode::RW),
min_free_space(0),
fd(-1),
path()
{
}
Branch2::Branch2(Branch2 const &v_)
: enabled(v_.enabled),
mode(v_.mode),
min_free_space(v_.min_free_space),
fd(v_.fd),
path(v_.path)
{
}
Branch2::Branch2(Branch2 &&v_)
: enabled(v_.enabled),
mode(v_.mode),
min_free_space(v_.min_free_space),
fd(v_.fd),
path(std::move(v_.path))
{
v_.fd = -1;
}
Branch2::Branch2(toml::value const &v_,
uint64_t const min_free_space_)
: mode(Mode::RW)
{
mode = Mode::_from_string(toml::find<std::string>(v_,"mode").c_str());
min_free_space = toml::find_or(v_,"min-free-space",min_free_space);
path = toml::find<std::string>(v_,"path");
int const flags = O_DIRECTORY | O_PATH | O_NOATIME;
fd = openat(AT_FDCWD,path.string().c_str(),flags);
}
Branch2::~Branch2()
{
if(fd >= 0)
close(fd);
}
Branch2&
Branch2::operator=(Branch2 const &b_)
{
mode = b_.mode;
min_free_space = b_.min_free_space;
path = b_.path;
fd = b_.fd;
return *this;
}
bool
Branch2::matches_exclude(ghc::filesystem::path const &path_) const
{
int rv;
const char *path;
path = path_.string().c_str();
for(auto const &pattern : exclude_patterns)
{
rv = ::fnmatch(pattern.c_str(),path,FNM_EXTMATCH);
if(rv == 0)
return true;
}
return false;
}
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include "ghc/filesystem.hpp"
#include "toml.hpp"
#include "enum.h"
#include <stdint.h>
BETTER_ENUM(BranchMode,int,RO,RW,NC);
class Branch2
{
public:
typedef BranchMode Mode;
public:
Branch2();
Branch2(Branch2 const &);
Branch2(Branch2 &&);
Branch2(toml::value const &value,
uint64_t const min_free_space = 0);
~Branch2();
Branch2& operator=(Branch2 const &);
public:
bool matches_exclude(ghc::filesystem::path const &path) const;
public:
bool enabled;
Mode mode;
uint64_t min_free_space;
int fd;
ghc::filesystem::path path;
std::vector<std::string> exclude_patterns;
};
+83
View File
@@ -0,0 +1,83 @@
#include "branch_tier.hpp"
#include "fmt/core.h"
#include "fs_glob.hpp"
#include <cassert>
namespace l
{
static
void
load_branch_literal(toml::table const &v_,
uint64_t const min_free_space_,
std::vector<Branch2> &branches_)
{
branches_.emplace_back(v_,min_free_space_);
}
static
void
load_branch_glob(toml::table const &v_,
uint64_t const min_free_space_,
std::vector<Branch2> &branches_)
{
toml::table table;
std::string pattern;
std::vector<std::string> paths;
pattern = v_.at("path").as_string();
fs::glob(pattern,&paths);
table = v_;
branches_.resize(branches_.size() + paths.size());
for(auto &path : paths)
{
table["path"] = path;
branches_.emplace_back(table,min_free_space_);
}
}
static
void
load_branch_scan(toml::table const &v_,
uint64_t const min_free_space_,
std::vector<Branch2> &branches_)
{
assert(("not currently supported",0));
}
}
BranchTier::BranchTier()
{
}
BranchTier::BranchTier(toml::value const &v_,
uint64_t const min_free_space_)
{
uint64_t min_free_space;
auto const &branches = toml::find(v_,"branch").as_array();
min_free_space = toml::find_or(v_,"min-free-space",min_free_space_);
for(auto const &branch : branches)
{
bool enabled;
std::string type;
auto const &table = branch.as_table();
enabled = toml::find_or(branch,"enabled",false);
if(!enabled)
continue;
type = table.at("type").as_string();
if(type == "literal")
l::load_branch_literal(table,min_free_space,_branches);
else if(type == "glob")
l::load_branch_glob(table,min_free_space,_branches);
else if(type == "scan")
l::load_branch_scan(table,min_free_space,_branches);
}
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "branch2.hpp"
#include "toml.hpp"
#include <vector>
class BranchTier
{
public:
BranchTier();
BranchTier(toml::value const &value,
uint64_t const min_free_space = 0);
public:
std::vector<Branch2>::iterator begin() { return _branches.begin(); }
std::vector<Branch2>::iterator end() { return _branches.end(); }
std::vector<Branch2>::const_iterator begin() const { return _branches.begin(); }
std::vector<Branch2>::const_iterator end() const { return _branches.end(); }
private:
std::vector<Branch2> _branches;
};
+56
View File
@@ -0,0 +1,56 @@
#include "branches2.hpp"
#include "fs_clonepath.hpp"
#include "fmt/core.h"
#include <iostream>
#include <errno.h>
Branches2::Branches2()
{
}
Branches2::Branches2(toml::value const &v_)
{
uint64_t min_free_space;
auto const &tiers = toml::find(v_,"tier");
min_free_space = toml::find_or(v_,"min-free-space",0);
for(auto const &tier : tiers.as_array())
{
bool enabled;
enabled = toml::find_or(tier,"enabled",false);
if(!enabled)
continue;
_branches.emplace_back(tier,min_free_space);
}
}
int
Branches2::clonepath(ghc::filesystem::path basepath_,
ghc::filesystem::path relpath_)
{
int rv;
for(auto const &tier : _branches)
{
for(auto const &branch : tier)
{
if(branch.path == basepath_)
continue;
rv = fs::clonepath(branch.path,basepath_,relpath_);
if(rv < 0)
continue;
return 0;
}
}
errno = ENOENT;
return -1;
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "branch2.hpp"
#include "branch_tier.hpp"
#include "toml.hpp"
#include <vector>
class Branches2
{
public:
Branches2();
Branches2(toml::value const &);
public:
int clonepath(ghc::filesystem::path basepath,
ghc::filesystem::path fusepath);
public:
std::vector<BranchTier>::iterator begin() { return _branches.begin(); }
std::vector<BranchTier>::iterator end() { return _branches.end(); }
std::vector<BranchTier>::const_iterator begin() const { return _branches.begin(); }
std::vector<BranchTier>::const_iterator end() const { return _branches.end(); }
private:
std::vector<BranchTier> _branches;
};
+1326
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -16,7 +16,10 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "fs_close.hpp"
#include "fs_fgetxattr.hpp"
#include "fs_lgetxattr.hpp"
#include "fs_openat.hpp"
#include "fs_path.hpp"
#include <string>
@@ -40,5 +43,23 @@ namespace fs
return (rv != -1);
}
bool
dir_has_defaults(const int dirfd_,
const ghc::filesystem::path &relpath_)
{
int rv;
int fd;
fd = fs::openat(dirfd_,relpath_.parent_path(),O_RDONLY|O_DIRECTORY);
if(fd < 0)
return false;
rv = fs::fgetxattr(fd,POSIX_ACL_DEFAULT_XATTR,NULL,0);
fs::close(fd);
return (rv != -1);
}
}
}
+38
View File
@@ -18,6 +18,8 @@
#pragma once
#include "ghc/filesystem.hpp"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -38,4 +40,40 @@ namespace fs
return ((rv == -1) ? -errno : rv);
}
static
inline
int
openat(const int dirfd_,
const char *pathname_,
const int flags_,
const mode_t mode_)
{
int rv;
rv = ::openat(dirfd_,pathname_,flags_,mode_);
return ((rv == -1) ? -errno : rv);
}
static
inline
int
openat(const int dirfd_,
const ghc::filesystem::path &pathname_,
const int flags_)
{
return fs::openat(dirfd_,pathname_.string().c_str(),flags_);
}
static
inline
int
openat(const int dirfd_,
const ghc::filesystem::path &pathname_,
const int flags_,
const mode_t mode_)
{
return fs::openat(dirfd_,pathname_.string().c_str(),flags_,mode_);
}
}
+52
View File
@@ -0,0 +1,52 @@
/*
ISC License
Copyright (c) 2024, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "func_create_base.hpp"
#include <memory>
namespace Func2
{
class Create
{
public:
Create&
operator=(std::unique_ptr<Func2::CreateBase> &&ptr_)
{
_func = std::move(ptr_);
return *this;
}
public:
int
operator()(Branches2 &branches_,
fs::Path const &fusepath_,
mode_t const mode_,
fuse_file_info_t *ffi_)
{
return (*_func)(branches_,fusepath_,mode_,ffi_);
}
private:
std::unique_ptr<Func2::CreateBase> _func;
};
}
+43
View File
@@ -0,0 +1,43 @@
/*
ISC License
Copyright (c) 2024, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "branches2.hpp"
#include "fs_path.hpp"
#include "fuse.h"
#include <sys/types.h>
namespace Func2
{
class CreateBase
{
public:
CreateBase() {};
virtual ~CreateBase() {};
public:
virtual int operator()(Branches2 &branches,
fs::Path const &fusepath,
mode_t const mode,
fuse_file_info_t *ffi) = 0;
};
}
+110
View File
@@ -0,0 +1,110 @@
#include "func_create_ff.hpp"
#include "fileinfo.hpp"
#include "fs_acl.hpp"
#include "fs_info.hpp"
#include "fs_open.hpp"
#include "fs_openat.hpp"
#include "func_open_create_utils.hpp"
#include "ugid.hpp"
#include <errno.h>
Func2::CreateFF::CreateFF()
{
}
Func2::CreateFF::CreateFF(toml::value const &v_)
{
}
Func2::CreateFF::~CreateFF()
{
}
namespace l
{
static
int
create(Branches2 &branches_,
fs::Path const &fusepath_,
mode_t const mode_,
mode_t const umask_,
fuse_file_info_t *ffi_)
{
int rv;
mode_t mode;
FileInfo *fi;
fs::info_t info;
fs::Path fullpath;
mode = mode_;
for(auto &tier : branches_)
{
for(auto &branch : tier)
{
if(branch.mode != +Branch2::Mode::RW)
continue;
if(branch.matches_exclude(fusepath_))
continue;
rv = fs::info(branch.path,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < branch.min_free_space)
continue;
fullpath = branch.path / fusepath_;
if(!fs::acl::dir_has_defaults(fullpath))
mode &= ~umask_;
rv = fs::openat(branch.fd,fusepath_,ffi_->flags,mode);
if((rv == -1) && (errno == ENOENT))
{
branches_.clonepath(branch.path,fusepath_);
rv = fs::openat(branch.fd,fusepath_,ffi_->flags,mode);
}
if((rv == -1) && (errno == EROFS))
{
branch.mode = Branch2::Mode::RO;
continue;
}
fi = new FileInfo(rv,fusepath_.string().c_str(),ffi_->direct_io);
ffi_->fh = reinterpret_cast<uint64_t>(fi);
return 0;
}
}
return rv;
}
}
int
Func2::CreateFF::operator()(Branches2 &branches_,
fs::Path const &fusepath_,
mode_t const mode_,
fuse_file_info_t *ffi_)
{
int rv;
Config::Read cfg;
fuse_context const *fc = fuse_get_context();
ugid::Set const ugid(fc->uid,fc->gid);
utils::cfg_to_ffi_flags(cfg,fc->pid,ffi_);
if(cfg->writeback_cache)
utils::tweak_flags_writeback_cache(&ffi_->flags);
ffi_->noflush = !utils::calculate_flush(cfg->flushonclose,ffi_->flags);
rv = l::create(branches_,fusepath_,mode_,fc->umask,ffi_);
return rv;
}
+41
View File
@@ -0,0 +1,41 @@
/*
ISC License
Copyright (c) 2024, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "func_create_base.hpp"
#include "toml.hpp"
namespace Func2
{
class CreateFF final : public Func2::CreateBase
{
public:
CreateFF();
CreateFF(toml::value const &);
~CreateFF();
public:
virtual int operator()(Branches2 &branches,
fs::Path const &fusepath,
mode_t const mode,
fuse_file_info_t *ffi);
};
}
+122
View File
@@ -0,0 +1,122 @@
/*
ISC License
Copyright (c) 2024, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "func_open_create_utils.hpp"
#include "procfs_get_name.hpp"
/*
The kernel expects being able to issue read requests when running
with writeback caching enabled so we must change O_WRONLY to
O_RDWR.
With writeback caching enabled the kernel handles O_APPEND. Could
be an issue if the underlying file changes out of band but that is
true of any caching.
*/
void
utils::tweak_flags_writeback_cache(int *flags_)
{
if((*flags_ & O_ACCMODE) == O_WRONLY)
*flags_ = ((*flags_ & ~O_ACCMODE) | O_RDWR);
if(*flags_ & O_APPEND)
*flags_ &= ~O_APPEND;
}
namespace l
{
static
bool
rdonly(const int flags_)
{
return ((flags_ & O_ACCMODE) == O_RDONLY);
}
}
bool
utils::calculate_flush(FlushOnClose flushonclose_,
int const flags_)
{
switch(flushonclose_)
{
case FlushOnCloseEnum::NEVER:
return false;
case FlushOnCloseEnum::OPENED_FOR_WRITE:
return !l::rdonly(flags_);
case FlushOnCloseEnum::ALWAYS:
return true;
}
return true;
}
void
utils::cfg_to_ffi_flags(Config::Read &cfg_,
int const tid_,
fuse_file_info_t *ffi_)
{
switch(cfg_->cache_files)
{
case CacheFiles::ENUM::LIBFUSE:
ffi_->direct_io = cfg_->direct_io;
ffi_->keep_cache = cfg_->kernel_cache;
ffi_->auto_cache = cfg_->auto_cache;
break;
case CacheFiles::ENUM::OFF:
ffi_->direct_io = 1;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
break;
case CacheFiles::ENUM::PARTIAL:
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
break;
case CacheFiles::ENUM::FULL:
ffi_->direct_io = 0;
ffi_->keep_cache = 1;
ffi_->auto_cache = 0;
break;
case CacheFiles::ENUM::AUTO_FULL:
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 1;
break;
case CacheFiles::ENUM::PER_PROCESS:
std::string proc_name;
proc_name = procfs::get_name(tid_);
if(cfg_->cache_files_process_names.count(proc_name) == 0)
{
ffi_->direct_io = 1;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
}
else
{
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
}
break;
}
if(cfg_->parallel_direct_writes == true)
ffi_->parallel_direct_writes = ffi_->direct_io;
}
+30
View File
@@ -0,0 +1,30 @@
/*
ISC License
Copyright (c) 2024, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "config.hpp"
#include "fuse.h"
namespace utils
{
void tweak_flags_writeback_cache(int *flags);
bool calculate_flush(FlushOnClose flushonclose, int const flags);
void cfg_to_ffi_flags(Config::Read &cfg, int const tid, fuse_file_info_t *ffi);
}
+5 -105
View File
@@ -21,8 +21,8 @@
#include "fs_clonepath.hpp"
#include "fs_open.hpp"
#include "fs_path.hpp"
#include "procfs_get_name.hpp"
#include "ugid.hpp"
#include "func_open_create_utils.hpp"
#include "fuse.h"
@@ -32,106 +32,6 @@
namespace l
{
/*
The kernel expects being able to issue read requests when running
with writeback caching enabled so we must change O_WRONLY to
O_RDWR.
With writeback caching enabled the kernel handles O_APPEND. Could
be an issue if the underlying file changes out of band but that is
true of any caching.
*/
static
void
tweak_flags_writeback_cache(int *flags_)
{
if((*flags_ & O_ACCMODE) == O_WRONLY)
*flags_ = ((*flags_ & ~O_ACCMODE) | O_RDWR);
if(*flags_ & O_APPEND)
*flags_ &= ~O_APPEND;
}
static
bool
rdonly(const int flags_)
{
return ((flags_ & O_ACCMODE) == O_RDONLY);
}
static
bool
calculate_flush(FlushOnClose const flushonclose_,
int const flags_)
{
switch(flushonclose_)
{
case FlushOnCloseEnum::NEVER:
return false;
case FlushOnCloseEnum::OPENED_FOR_WRITE:
return !l::rdonly(flags_);
case FlushOnCloseEnum::ALWAYS:
return true;
}
return true;
}
static
void
config_to_ffi_flags(Config::Read &cfg_,
const int tid_,
fuse_file_info_t *ffi_)
{
switch(cfg_->cache_files)
{
case CacheFiles::ENUM::LIBFUSE:
ffi_->direct_io = cfg_->direct_io;
ffi_->keep_cache = cfg_->kernel_cache;
ffi_->auto_cache = cfg_->auto_cache;
break;
case CacheFiles::ENUM::OFF:
ffi_->direct_io = 1;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
break;
case CacheFiles::ENUM::PARTIAL:
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
break;
case CacheFiles::ENUM::FULL:
ffi_->direct_io = 0;
ffi_->keep_cache = 1;
ffi_->auto_cache = 0;
break;
case CacheFiles::ENUM::AUTO_FULL:
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 1;
break;
case CacheFiles::ENUM::PER_PROCESS:
std::string proc_name;
proc_name = procfs::get_name(tid_);
if(cfg_->cache_files_process_names.count(proc_name) == 0)
{
ffi_->direct_io = 1;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
}
else
{
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
}
break;
}
if(cfg_->parallel_direct_writes == true)
ffi_->parallel_direct_writes = ffi_->direct_io;
}
static
int
create_core(const std::string &fullpath_,
@@ -220,13 +120,13 @@ namespace FUSE
const fuse_context *fc = fuse_get_context();
const ugid::Set ugid(fc->uid,fc->gid);
l::config_to_ffi_flags(cfg,fc->pid,ffi_);
utils::cfg_to_ffi_flags(cfg,fc->pid,ffi_);
if(cfg->writeback_cache)
l::tweak_flags_writeback_cache(&ffi_->flags);
utils::tweak_flags_writeback_cache(&ffi_->flags);
ffi_->noflush = !l::calculate_flush(cfg->flushonclose,
ffi_->flags);
ffi_->noflush = !utils::calculate_flush(cfg->flushonclose,
ffi_->flags);
rv = l::create(cfg->func.getattr.policy,
cfg->func.create.policy,
+41
View File
@@ -0,0 +1,41 @@
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
namespace std
{
template<class T> struct _Unique_if
{
typedef unique_ptr<T> _Single_object;
};
template<class T> struct _Unique_if<T[]>
{
typedef unique_ptr<T[]> _Unknown_bound;
};
template<class T, size_t N> struct _Unique_if<T[N]>
{
typedef void _Known_bound;
};
template<class T, class... Args>
typename _Unique_if<T>::_Single_object
make_unique(Args&&... args)
{
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<class T>
typename _Unique_if<T>::_Unknown_bound
make_unique(size_t n)
{
typedef typename remove_extent<T>::type U;
return unique_ptr<T>(new U[n]());
}
template<class T, class... Args>
typename _Unique_if<T>::_Known_bound
make_unique(Args&&...) = delete;
}
+26
View File
@@ -14,6 +14,10 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "state.hpp"
#include "toml.hpp"
#include "fs_readahead.hpp"
#include "fs_wait_for_mount.hpp"
#include "syslog.hpp"
@@ -294,9 +298,31 @@ namespace l
}
}
int
main(int argc_,
char **argv_)
{
State state;
auto data = toml::parse<toml::preserve_comments>("config.toml");
fuse_file_info_t ffi;
Branches2 branches(data["branches"]);
state.branches = branches;
std::cout << data["branches"] << '\n';
for(auto &branch_tier : branches)
{
for(auto &branch : branch_tier)
{
fmt::print("{}\n",branch.path.string());
}
}
pause();
return 0;
return l::main(argc_,argv_);
}
+12
View File
@@ -0,0 +1,12 @@
#include "state.hpp"
#include "func_create_ff.hpp"
#include "make_unique.hpp"
State state;
State::State()
{
_create = std::make_unique<Func2::CreateFF>();
}
+27
View File
@@ -0,0 +1,27 @@
#include "branches2.hpp"
#include "func_create.hpp"
#include <memory>
class State
{
typedef ghc::filesystem::path Path;
public:
State();
public:
Branches2 branches;
public:
int
create(Path const &fusepath_,
mode_t const mode_,
fuse_file_info_t *ffi_)
{
return _create(branches,fusepath_,mode_,ffi_);
}
private:
Func2::Create _create;
};
+38
View File
@@ -0,0 +1,38 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Toru Niina
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef TOML_FOR_MODERN_CPP
#define TOML_FOR_MODERN_CPP
#define TOML11_VERSION_MAJOR 3
#define TOML11_VERSION_MINOR 8
#define TOML11_VERSION_PATCH 1
#include "toml/parser.hpp"
#include "toml/literal.hpp"
#include "toml/serializer.hpp"
#include "toml/get.hpp"
#include "toml/macros.hpp"
#endif// TOML_FOR_MODERN_CPP
+109
View File
@@ -0,0 +1,109 @@
#ifndef TOML11_COLOR_HPP
#define TOML11_COLOR_HPP
#include <cstdint>
#include <ostream>
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
#define TOML11_ERROR_MESSAGE_COLORIZED true
#else
#define TOML11_ERROR_MESSAGE_COLORIZED false
#endif
namespace toml
{
// put ANSI escape sequence to ostream
namespace color_ansi
{
namespace detail
{
inline int colorize_index()
{
static const int index = std::ios_base::xalloc();
return index;
}
// Control color mode globally
class color_mode
{
public:
inline void enable()
{
should_color_ = true;
}
inline void disable()
{
should_color_ = false;
}
inline bool should_color() const
{
return should_color_;
}
static color_mode& status()
{
static color_mode status_;
return status_;
}
private:
bool should_color_ = false;
};
} // detail
inline std::ostream& colorize(std::ostream& os)
{
// by default, it is zero.
os.iword(detail::colorize_index()) = 1;
return os;
}
inline std::ostream& nocolorize(std::ostream& os)
{
os.iword(detail::colorize_index()) = 0;
return os;
}
inline std::ostream& reset (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;}
inline std::ostream& bold (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;}
inline std::ostream& grey (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;}
inline std::ostream& red (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;}
inline std::ostream& green (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;}
inline std::ostream& yellow (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;}
inline std::ostream& blue (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;}
inline std::ostream& magenta(std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;}
inline std::ostream& cyan (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;}
inline std::ostream& white (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;}
inline void enable()
{
return detail::color_mode::status().enable();
}
inline void disable()
{
return detail::color_mode::status().disable();
}
inline bool should_color()
{
return detail::color_mode::status().should_color();
}
} // color_ansi
// ANSI escape sequence is the only and default colorization method currently
namespace color = color_ansi;
} // toml
#endif// TOML11_COLOR_HPP
+306
View File
@@ -0,0 +1,306 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_COMBINATOR_HPP
#define TOML11_COMBINATOR_HPP
#include <cassert>
#include <cctype>
#include <cstdio>
#include <array>
#include <iomanip>
#include <iterator>
#include <limits>
#include <type_traits>
#include "region.hpp"
#include "result.hpp"
#include "traits.hpp"
#include "utility.hpp"
// they scans characters and returns region if it matches to the condition.
// when they fail, it does not change the location.
// in lexer.hpp, these are used.
namespace toml
{
namespace detail
{
// to output character as an error message.
inline std::string show_char(const char c)
{
// It suppresses an error that occurs only in Debug mode of MSVC++ on Windows.
// I'm not completely sure but they check the value of char to be in the
// range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes
// has negative value (if char has sign). So here it re-interprets c as
// unsigned char through pointer. In general, converting pointer to a
// pointer that has different type cause UB, but `(signed|unsigned)?char`
// are one of the exceptions. Converting pointer only to char and std::byte
// (c++17) are valid.
if(std::isgraph(*reinterpret_cast<unsigned char const*>(std::addressof(c))))
{
return std::string(1, c);
}
else
{
std::array<char, 5> buf;
buf.fill('\0');
const auto r = std::snprintf(
buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
(void) r; // Unused variable warning
assert(r == static_cast<int>(buf.size()) - 1);
return std::string(buf.data());
}
}
template<char C>
struct character
{
static constexpr char target = C;
static result<region, none_t>
invoke(location& loc)
{
if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter();
const char c = *(loc.iter());
if(c != target)
{
return none();
}
loc.advance(); // update location
return ok(region(loc, first, loc.iter()));
}
};
template<char C>
constexpr char character<C>::target;
// closed interval [Low, Up]. both Low and Up are included.
template<char Low, char Up>
struct in_range
{
// assuming ascii part of UTF-8...
static_assert(Low <= Up, "lower bound should be less than upper bound.");
static constexpr char upper = Up;
static constexpr char lower = Low;
static result<region, none_t>
invoke(location& loc)
{
if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter();
const char c = *(loc.iter());
if(c < lower || upper < c)
{
return none();
}
loc.advance();
return ok(region(loc, first, loc.iter()));
}
};
template<char L, char U> constexpr char in_range<L, U>::upper;
template<char L, char U> constexpr char in_range<L, U>::lower;
// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char.
// for detecting invalid characters, like control sequences in toml string.
template<typename Combinator>
struct exclude
{
static result<region, none_t>
invoke(location& loc)
{
if(loc.iter() == loc.end()) {return none();}
auto first = loc.iter();
auto rslt = Combinator::invoke(loc);
if(rslt.is_ok())
{
loc.reset(first);
return none();
}
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
return ok(region(loc, first, loc.iter()));
}
};
// increment `iter`, if matches. otherwise, just return empty string.
template<typename Combinator>
struct maybe
{
static result<region, none_t>
invoke(location& loc)
{
const auto rslt = Combinator::invoke(loc);
if(rslt.is_ok())
{
return rslt;
}
return ok(region(loc));
}
};
template<typename ... Ts>
struct sequence;
template<typename Head, typename ... Tail>
struct sequence<Head, Tail...>
{
static result<region, none_t>
invoke(location& loc)
{
const auto first = loc.iter();
auto rslt = Head::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first);
}
// called from the above function only, recursively.
template<typename Iterator>
static result<region, none_t>
invoke(location& loc, region reg, Iterator first)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
reg += rslt.unwrap(); // concat regions
return sequence<Tail...>::invoke(loc, std::move(reg), first);
}
};
template<typename Head>
struct sequence<Head>
{
// would be called from sequence<T ...>::invoke only.
template<typename Iterator>
static result<region, none_t>
invoke(location& loc, region reg, Iterator first)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
reg += rslt.unwrap(); // concat regions
return ok(reg);
}
};
template<typename ... Ts>
struct either;
template<typename Head, typename ... Tail>
struct either<Head, Tail...>
{
static result<region, none_t>
invoke(location& loc)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_ok()) {return rslt;}
return either<Tail...>::invoke(loc);
}
};
template<typename Head>
struct either<Head>
{
static result<region, none_t>
invoke(location& loc)
{
return Head::invoke(loc);
}
};
template<typename T, typename N>
struct repeat;
template<std::size_t N> struct exactly{};
template<std::size_t N> struct at_least{};
struct unlimited{};
template<typename T, std::size_t N>
struct repeat<T, exactly<N>>
{
static result<region, none_t>
invoke(location& loc)
{
region retval(loc);
const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i)
{
auto rslt = T::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
retval += rslt.unwrap();
}
return ok(std::move(retval));
}
};
template<typename T, std::size_t N>
struct repeat<T, at_least<N>>
{
static result<region, none_t>
invoke(location& loc)
{
region retval(loc);
const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i)
{
auto rslt = T::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
retval += rslt.unwrap();
}
while(true)
{
auto rslt = T::invoke(loc);
if(rslt.is_err())
{
return ok(std::move(retval));
}
retval += rslt.unwrap();
}
}
};
template<typename T>
struct repeat<T, unlimited>
{
static result<region, none_t>
invoke(location& loc)
{
region retval(loc);
while(true)
{
auto rslt = T::invoke(loc);
if(rslt.is_err())
{
return ok(std::move(retval));
}
retval += rslt.unwrap();
}
}
};
} // detail
} // toml
#endif// TOML11_COMBINATOR_HPP
+484
View File
@@ -0,0 +1,484 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_COMMENTS_HPP
#define TOML11_COMMENTS_HPP
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments
#else
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments
#endif
// This file provides mainly two classes, `preserve_comments` and `discard_comments`.
// Those two are a container that have the same interface as `std::vector<std::string>`
// but bahaves in the opposite way. `preserve_comments` is just the same as
// `std::vector<std::string>` and each `std::string` corresponds to a comment line.
// Conversely, `discard_comments` discards all the strings and ignores everything
// assigned in it. `discard_comments` is always empty and you will encounter an
// error whenever you access to the element.
namespace toml
{
struct discard_comments; // forward decl
// use it in the following way
//
// const toml::basic_value<toml::preserve_comments> data =
// toml::parse<toml::preserve_comments>("example.toml");
//
// the interface is almost the same as std::vector<std::string>.
struct preserve_comments
{
// `container_type` is not provided in discard_comments.
// do not use this inner-type in a generic code.
using container_type = std::vector<std::string>;
using size_type = container_type::size_type;
using difference_type = container_type::difference_type;
using value_type = container_type::value_type;
using reference = container_type::reference;
using const_reference = container_type::const_reference;
using pointer = container_type::pointer;
using const_pointer = container_type::const_pointer;
using iterator = container_type::iterator;
using const_iterator = container_type::const_iterator;
using reverse_iterator = container_type::reverse_iterator;
using const_reverse_iterator = container_type::const_reverse_iterator;
preserve_comments() = default;
~preserve_comments() = default;
preserve_comments(preserve_comments const&) = default;
preserve_comments(preserve_comments &&) = default;
preserve_comments& operator=(preserve_comments const&) = default;
preserve_comments& operator=(preserve_comments &&) = default;
explicit preserve_comments(const std::vector<std::string>& c): comments(c){}
explicit preserve_comments(std::vector<std::string>&& c)
: comments(std::move(c))
{}
preserve_comments& operator=(const std::vector<std::string>& c)
{
comments = c;
return *this;
}
preserve_comments& operator=(std::vector<std::string>&& c)
{
comments = std::move(c);
return *this;
}
explicit preserve_comments(const discard_comments&) {}
explicit preserve_comments(size_type n): comments(n) {}
preserve_comments(size_type n, const std::string& x): comments(n, x) {}
preserve_comments(std::initializer_list<std::string> x): comments(x) {}
template<typename InputIterator>
preserve_comments(InputIterator first, InputIterator last)
: comments(first, last)
{}
template<typename InputIterator>
void assign(InputIterator first, InputIterator last) {comments.assign(first, last);}
void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
void assign(size_type n, const std::string& val) {comments.assign(n, val);}
// Related to the issue #97.
//
// It is known that `std::vector::insert` and `std::vector::erase` in
// the standard library implementation included in GCC 4.8.5 takes
// `std::vector::iterator` instead of `std::vector::const_iterator`.
// Because of the const-correctness, we cannot convert a `const_iterator` to
// an `iterator`. It causes compilation error in GCC 4.8.5.
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__)
# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805
# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
# endif
#endif
#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
iterator insert(iterator p, const std::string& x)
{
return comments.insert(p, x);
}
iterator insert(iterator p, std::string&& x)
{
return comments.insert(p, std::move(x));
}
void insert(iterator p, size_type n, const std::string& x)
{
return comments.insert(p, n, x);
}
template<typename InputIterator>
void insert(iterator p, InputIterator first, InputIterator last)
{
return comments.insert(p, first, last);
}
void insert(iterator p, std::initializer_list<std::string> ini)
{
return comments.insert(p, ini);
}
template<typename ... Ts>
iterator emplace(iterator p, Ts&& ... args)
{
return comments.emplace(p, std::forward<Ts>(args)...);
}
iterator erase(iterator pos) {return comments.erase(pos);}
iterator erase(iterator first, iterator last)
{
return comments.erase(first, last);
}
#else
iterator insert(const_iterator p, const std::string& x)
{
return comments.insert(p, x);
}
iterator insert(const_iterator p, std::string&& x)
{
return comments.insert(p, std::move(x));
}
iterator insert(const_iterator p, size_type n, const std::string& x)
{
return comments.insert(p, n, x);
}
template<typename InputIterator>
iterator insert(const_iterator p, InputIterator first, InputIterator last)
{
return comments.insert(p, first, last);
}
iterator insert(const_iterator p, std::initializer_list<std::string> ini)
{
return comments.insert(p, ini);
}
template<typename ... Ts>
iterator emplace(const_iterator p, Ts&& ... args)
{
return comments.emplace(p, std::forward<Ts>(args)...);
}
iterator erase(const_iterator pos) {return comments.erase(pos);}
iterator erase(const_iterator first, const_iterator last)
{
return comments.erase(first, last);
}
#endif
void swap(preserve_comments& other) {comments.swap(other.comments);}
void push_back(const std::string& v) {comments.push_back(v);}
void push_back(std::string&& v) {comments.push_back(std::move(v));}
void pop_back() {comments.pop_back();}
template<typename ... Ts>
void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward<Ts>(args)...);}
void clear() {comments.clear();}
size_type size() const noexcept {return comments.size();}
size_type max_size() const noexcept {return comments.max_size();}
size_type capacity() const noexcept {return comments.capacity();}
bool empty() const noexcept {return comments.empty();}
void reserve(size_type n) {comments.reserve(n);}
void resize(size_type n) {comments.resize(n);}
void resize(size_type n, const std::string& c) {comments.resize(n, c);}
void shrink_to_fit() {comments.shrink_to_fit();}
reference operator[](const size_type n) noexcept {return comments[n];}
const_reference operator[](const size_type n) const noexcept {return comments[n];}
reference at(const size_type n) {return comments.at(n);}
const_reference at(const size_type n) const {return comments.at(n);}
reference front() noexcept {return comments.front();}
const_reference front() const noexcept {return comments.front();}
reference back() noexcept {return comments.back();}
const_reference back() const noexcept {return comments.back();}
pointer data() noexcept {return comments.data();}
const_pointer data() const noexcept {return comments.data();}
iterator begin() noexcept {return comments.begin();}
iterator end() noexcept {return comments.end();}
const_iterator begin() const noexcept {return comments.begin();}
const_iterator end() const noexcept {return comments.end();}
const_iterator cbegin() const noexcept {return comments.cbegin();}
const_iterator cend() const noexcept {return comments.cend();}
reverse_iterator rbegin() noexcept {return comments.rbegin();}
reverse_iterator rend() noexcept {return comments.rend();}
const_reverse_iterator rbegin() const noexcept {return comments.rbegin();}
const_reverse_iterator rend() const noexcept {return comments.rend();}
const_reverse_iterator crbegin() const noexcept {return comments.crbegin();}
const_reverse_iterator crend() const noexcept {return comments.crend();}
friend bool operator==(const preserve_comments&, const preserve_comments&);
friend bool operator!=(const preserve_comments&, const preserve_comments&);
friend bool operator< (const preserve_comments&, const preserve_comments&);
friend bool operator<=(const preserve_comments&, const preserve_comments&);
friend bool operator> (const preserve_comments&, const preserve_comments&);
friend bool operator>=(const preserve_comments&, const preserve_comments&);
friend void swap(preserve_comments&, std::vector<std::string>&);
friend void swap(std::vector<std::string>&, preserve_comments&);
private:
container_type comments;
};
inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;}
inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;}
inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;}
inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;}
inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;}
inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;}
inline void swap(preserve_comments& lhs, preserve_comments& rhs)
{
lhs.swap(rhs);
return;
}
inline void swap(preserve_comments& lhs, std::vector<std::string>& rhs)
{
lhs.comments.swap(rhs);
return;
}
inline void swap(std::vector<std::string>& lhs, preserve_comments& rhs)
{
lhs.swap(rhs.comments);
return;
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const preserve_comments& com)
{
for(const auto& c : com)
{
os << '#' << c << '\n';
}
return os;
}
namespace detail
{
// To provide the same interface with `preserve_comments`, `discard_comments`
// should have an iterator. But it does not contain anything, so we need to
// add an iterator that points nothing.
//
// It always points null, so DO NOT unwrap this iterator. It always crashes
// your program.
template<typename T, bool is_const>
struct empty_iterator
{
using value_type = T;
using reference_type = typename std::conditional<is_const, T const&, T&>::type;
using pointer_type = typename std::conditional<is_const, T const*, T*>::type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
empty_iterator() = default;
~empty_iterator() = default;
empty_iterator(empty_iterator const&) = default;
empty_iterator(empty_iterator &&) = default;
empty_iterator& operator=(empty_iterator const&) = default;
empty_iterator& operator=(empty_iterator &&) = default;
// DO NOT call these operators.
reference_type operator*() const noexcept {std::terminate();}
pointer_type operator->() const noexcept {return nullptr;}
reference_type operator[](difference_type) const noexcept {return this->operator*();}
// These operators do nothing.
empty_iterator& operator++() noexcept {return *this;}
empty_iterator operator++(int) noexcept {return *this;}
empty_iterator& operator--() noexcept {return *this;}
empty_iterator operator--(int) noexcept {return *this;}
empty_iterator& operator+=(difference_type) noexcept {return *this;}
empty_iterator& operator-=(difference_type) noexcept {return *this;}
empty_iterator operator+(difference_type) const noexcept {return *this;}
empty_iterator operator-(difference_type) const noexcept {return *this;}
};
template<typename T, bool C>
bool operator==(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
template<typename T, bool C>
bool operator!=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
template<typename T, bool C>
bool operator< (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
template<typename T, bool C>
bool operator<=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
template<typename T, bool C>
bool operator> (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
template<typename T, bool C>
bool operator>=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
template<typename T, bool C>
typename empty_iterator<T, C>::difference_type
operator-(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return 0;}
template<typename T, bool C>
empty_iterator<T, C>
operator+(typename empty_iterator<T, C>::difference_type, const empty_iterator<T, C>& rhs) noexcept {return rhs;}
template<typename T, bool C>
empty_iterator<T, C>
operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::difference_type) noexcept {return lhs;}
} // detail
// The default comment type. It discards all the comments. It requires only one
// byte to contain, so the memory footprint is smaller than preserve_comments.
//
// It just ignores `push_back`, `insert`, `erase`, and any other modifications.
// IT always returns size() == 0, the iterator taken by `begin()` is always the
// same as that of `end()`, and accessing through `operator[]` or iterators
// always causes a segmentation fault. DO NOT access to the element of this.
//
// Why this is chose as the default type is because the last version (2.x.y)
// does not contain any comments in a value. To minimize the impact on the
// efficiency, this is chosen as a default.
//
// To reduce the memory footprint, later we can try empty base optimization (EBO).
struct discard_comments
{
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using value_type = std::string;
using reference = std::string&;
using const_reference = std::string const&;
using pointer = std::string*;
using const_pointer = std::string const*;
using iterator = detail::empty_iterator<std::string, false>;
using const_iterator = detail::empty_iterator<std::string, true>;
using reverse_iterator = detail::empty_iterator<std::string, false>;
using const_reverse_iterator = detail::empty_iterator<std::string, true>;
discard_comments() = default;
~discard_comments() = default;
discard_comments(discard_comments const&) = default;
discard_comments(discard_comments &&) = default;
discard_comments& operator=(discard_comments const&) = default;
discard_comments& operator=(discard_comments &&) = default;
explicit discard_comments(const std::vector<std::string>&) noexcept {}
explicit discard_comments(std::vector<std::string>&&) noexcept {}
discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;}
discard_comments& operator=(std::vector<std::string>&&) noexcept {return *this;}
explicit discard_comments(const preserve_comments&) noexcept {}
explicit discard_comments(size_type) noexcept {}
discard_comments(size_type, const std::string&) noexcept {}
discard_comments(std::initializer_list<std::string>) noexcept {}
template<typename InputIterator>
discard_comments(InputIterator, InputIterator) noexcept {}
template<typename InputIterator>
void assign(InputIterator, InputIterator) noexcept {}
void assign(std::initializer_list<std::string>) noexcept {}
void assign(size_type, const std::string&) noexcept {}
iterator insert(const_iterator, const std::string&) {return iterator{};}
iterator insert(const_iterator, std::string&&) {return iterator{};}
iterator insert(const_iterator, size_type, const std::string&) {return iterator{};}
template<typename InputIterator>
iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};}
iterator insert(const_iterator, std::initializer_list<std::string>) {return iterator{};}
template<typename ... Ts>
iterator emplace(const_iterator, Ts&& ...) {return iterator{};}
iterator erase(const_iterator) {return iterator{};}
iterator erase(const_iterator, const_iterator) {return iterator{};}
void swap(discard_comments&) {return;}
void push_back(const std::string&) {return;}
void push_back(std::string&& ) {return;}
void pop_back() {return;}
template<typename ... Ts>
void emplace_back(Ts&& ...) {return;}
void clear() {return;}
size_type size() const noexcept {return 0;}
size_type max_size() const noexcept {return 0;}
size_type capacity() const noexcept {return 0;}
bool empty() const noexcept {return true;}
void reserve(size_type) {return;}
void resize(size_type) {return;}
void resize(size_type, const std::string&) {return;}
void shrink_to_fit() {return;}
// DO NOT access to the element of this container. This container is always
// empty, so accessing through operator[], front/back, data causes address
// error.
reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");}
const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");}
reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");}
const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");}
reference front() noexcept {never_call("toml::discard_comment::front");}
const_reference front() const noexcept {never_call("toml::discard_comment::front");}
reference back() noexcept {never_call("toml::discard_comment::back");}
const_reference back() const noexcept {never_call("toml::discard_comment::back");}
pointer data() noexcept {return nullptr;}
const_pointer data() const noexcept {return nullptr;}
iterator begin() noexcept {return iterator{};}
iterator end() noexcept {return iterator{};}
const_iterator begin() const noexcept {return const_iterator{};}
const_iterator end() const noexcept {return const_iterator{};}
const_iterator cbegin() const noexcept {return const_iterator{};}
const_iterator cend() const noexcept {return const_iterator{};}
reverse_iterator rbegin() noexcept {return iterator{};}
reverse_iterator rend() noexcept {return iterator{};}
const_reverse_iterator rbegin() const noexcept {return const_iterator{};}
const_reverse_iterator rend() const noexcept {return const_iterator{};}
const_reverse_iterator crbegin() const noexcept {return const_iterator{};}
const_reverse_iterator crend() const noexcept {return const_iterator{};}
private:
[[noreturn]] static void never_call(const char *const this_function)
{
#ifdef __has_builtin
# if __has_builtin(__builtin_unreachable)
__builtin_unreachable();
# endif
#endif
throw std::logic_error{this_function};
}
};
inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;}
inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;}
inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;}
inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;}
inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;}
inline void swap(const discard_comments&, const discard_comments&) noexcept {return;}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const discard_comments&)
{
return os;
}
} // toml11
#endif// TOML11_COMMENTS_HPP
+631
View File
@@ -0,0 +1,631 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_DATETIME_HPP
#define TOML11_DATETIME_HPP
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <array>
#include <chrono>
#include <iomanip>
#include <ostream>
#include <tuple>
namespace toml
{
// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
// provided in the absolutely same purpose, but C++11 is actually not compatible
// with C11. We need to dispatch the function depending on the OS.
namespace detail
{
// TODO: find more sophisticated way to handle this
#if defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_s(&dst, src);
if (result) { throw std::runtime_error("localtime_s failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_s(&dst, src);
if (result) { throw std::runtime_error("gmtime_s failed."); }
return dst;
}
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#else // fallback. not threadsafe
inline std::tm localtime_s(const std::time_t* src)
{
const auto result = std::localtime(src);
if (!result) { throw std::runtime_error("localtime failed."); }
return *result;
}
inline std::tm gmtime_s(const std::time_t* src)
{
const auto result = std::gmtime(src);
if (!result) { throw std::runtime_error("gmtime failed."); }
return *result;
}
#endif
} // detail
enum class month_t : std::uint8_t
{
Jan = 0,
Feb = 1,
Mar = 2,
Apr = 3,
May = 4,
Jun = 5,
Jul = 6,
Aug = 7,
Sep = 8,
Oct = 9,
Nov = 10,
Dec = 11
};
struct local_date
{
std::int16_t year{}; // A.D. (like, 2018)
std::uint8_t month{}; // [0, 11]
std::uint8_t day{}; // [1, 31]
local_date(int y, month_t m, int d)
: year (static_cast<std::int16_t>(y)),
month(static_cast<std::uint8_t>(m)),
day (static_cast<std::uint8_t>(d))
{}
explicit local_date(const std::tm& t)
: year (static_cast<std::int16_t>(t.tm_year + 1900)),
month(static_cast<std::uint8_t>(t.tm_mon)),
day (static_cast<std::uint8_t>(t.tm_mday))
{}
explicit local_date(const std::chrono::system_clock::time_point& tp)
{
const auto t = std::chrono::system_clock::to_time_t(tp);
const auto time = detail::localtime_s(&t);
*this = local_date(time);
}
explicit local_date(const std::time_t t)
: local_date(std::chrono::system_clock::from_time_t(t))
{}
operator std::chrono::system_clock::time_point() const
{
// std::mktime returns date as local time zone. no conversion needed
std::tm t;
t.tm_sec = 0;
t.tm_min = 0;
t.tm_hour = 0;
t.tm_mday = static_cast<int>(this->day);
t.tm_mon = static_cast<int>(this->month);
t.tm_year = static_cast<int>(this->year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
return std::chrono::system_clock::from_time_t(std::mktime(&t));
}
operator std::time_t() const
{
return std::chrono::system_clock::to_time_t(
std::chrono::system_clock::time_point(*this));
}
local_date() = default;
~local_date() = default;
local_date(local_date const&) = default;
local_date(local_date&&) = default;
local_date& operator=(local_date const&) = default;
local_date& operator=(local_date&&) = default;
};
inline bool operator==(const local_date& lhs, const local_date& rhs)
{
return std::make_tuple(lhs.year, lhs.month, lhs.day) ==
std::make_tuple(rhs.year, rhs.month, rhs.day);
}
inline bool operator!=(const local_date& lhs, const local_date& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const local_date& lhs, const local_date& rhs)
{
return std::make_tuple(lhs.year, lhs.month, lhs.day) <
std::make_tuple(rhs.year, rhs.month, rhs.day);
}
inline bool operator<=(const local_date& lhs, const local_date& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const local_date& lhs, const local_date& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const local_date& lhs, const local_date& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
{
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ;
return os;
}
struct local_time
{
std::uint8_t hour{}; // [0, 23]
std::uint8_t minute{}; // [0, 59]
std::uint8_t second{}; // [0, 60]
std::uint16_t millisecond{}; // [0, 999]
std::uint16_t microsecond{}; // [0, 999]
std::uint16_t nanosecond{}; // [0, 999]
local_time(int h, int m, int s,
int ms = 0, int us = 0, int ns = 0)
: hour (static_cast<std::uint8_t>(h)),
minute(static_cast<std::uint8_t>(m)),
second(static_cast<std::uint8_t>(s)),
millisecond(static_cast<std::uint16_t>(ms)),
microsecond(static_cast<std::uint16_t>(us)),
nanosecond (static_cast<std::uint16_t>(ns))
{}
explicit local_time(const std::tm& t)
: hour (static_cast<std::uint8_t>(t.tm_hour)),
minute(static_cast<std::uint8_t>(t.tm_min)),
second(static_cast<std::uint8_t>(t.tm_sec)),
millisecond(0), microsecond(0), nanosecond(0)
{}
template<typename Rep, typename Period>
explicit local_time(const std::chrono::duration<Rep, Period>& t)
{
const auto h = std::chrono::duration_cast<std::chrono::hours>(t);
this->hour = static_cast<std::uint8_t>(h.count());
const auto t2 = t - h;
const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2);
this->minute = static_cast<std::uint8_t>(m.count());
const auto t3 = t2 - m;
const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3);
this->second = static_cast<std::uint8_t>(s.count());
const auto t4 = t3 - s;
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4);
this->millisecond = static_cast<std::uint16_t>(ms.count());
const auto t5 = t4 - ms;
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5);
this->microsecond = static_cast<std::uint16_t>(us.count());
const auto t6 = t5 - us;
const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6);
this->nanosecond = static_cast<std::uint16_t>(ns.count());
}
operator std::chrono::nanoseconds() const
{
return std::chrono::nanoseconds (this->nanosecond) +
std::chrono::microseconds(this->microsecond) +
std::chrono::milliseconds(this->millisecond) +
std::chrono::seconds(this->second) +
std::chrono::minutes(this->minute) +
std::chrono::hours(this->hour);
}
local_time() = default;
~local_time() = default;
local_time(local_time const&) = default;
local_time(local_time&&) = default;
local_time& operator=(local_time const&) = default;
local_time& operator=(local_time&&) = default;
};
inline bool operator==(const local_time& lhs, const local_time& rhs)
{
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) ==
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
}
inline bool operator!=(const local_time& lhs, const local_time& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const local_time& lhs, const local_time& rhs)
{
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) <
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
}
inline bool operator<=(const local_time& lhs, const local_time& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const local_time& lhs, const local_time& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const local_time& lhs, const local_time& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_time& time)
{
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.hour ) << ':';
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.minute) << ':';
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.second);
if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0)
{
os << '.';
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.millisecond);
if(time.microsecond != 0 || time.nanosecond != 0)
{
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.microsecond);
if(time.nanosecond != 0)
{
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.nanosecond);
}
}
}
return os;
}
struct time_offset
{
std::int8_t hour{}; // [-12, 12]
std::int8_t minute{}; // [-59, 59]
time_offset(int h, int m)
: hour (static_cast<std::int8_t>(h)),
minute(static_cast<std::int8_t>(m))
{}
operator std::chrono::minutes() const
{
return std::chrono::minutes(this->minute) +
std::chrono::hours(this->hour);
}
time_offset() = default;
~time_offset() = default;
time_offset(time_offset const&) = default;
time_offset(time_offset&&) = default;
time_offset& operator=(time_offset const&) = default;
time_offset& operator=(time_offset&&) = default;
};
inline bool operator==(const time_offset& lhs, const time_offset& rhs)
{
return std::make_tuple(lhs.hour, lhs.minute) ==
std::make_tuple(rhs.hour, rhs.minute);
}
inline bool operator!=(const time_offset& lhs, const time_offset& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const time_offset& lhs, const time_offset& rhs)
{
return std::make_tuple(lhs.hour, lhs.minute) <
std::make_tuple(rhs.hour, rhs.minute);
}
inline bool operator<=(const time_offset& lhs, const time_offset& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const time_offset& lhs, const time_offset& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const time_offset& lhs, const time_offset& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
{
if(offset.hour == 0 && offset.minute == 0)
{
os << 'Z';
return os;
}
int minute = static_cast<int>(offset.hour) * 60 + offset.minute;
if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';}
os << std::setfill('0') << std::setw(2) << minute / 60 << ':';
os << std::setfill('0') << std::setw(2) << minute % 60;
return os;
}
struct local_datetime
{
local_date date{};
local_time time{};
local_datetime(local_date d, local_time t): date(d), time(t) {}
explicit local_datetime(const std::tm& t): date(t), time(t){}
explicit local_datetime(const std::chrono::system_clock::time_point& tp)
{
const auto t = std::chrono::system_clock::to_time_t(tp);
std::tm ltime = detail::localtime_s(&t);
this->date = local_date(ltime);
this->time = local_time(ltime);
// std::tm lacks subsecond information, so diff between tp and tm
// can be used to get millisecond & microsecond information.
const auto t_diff = tp -
std::chrono::system_clock::from_time_t(std::mktime(&ltime));
this->time.millisecond = static_cast<std::uint16_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count());
this->time.microsecond = static_cast<std::uint16_t>(
std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count());
this->time.nanosecond = static_cast<std::uint16_t>(
std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count());
}
explicit local_datetime(const std::time_t t)
: local_datetime(std::chrono::system_clock::from_time_t(t))
{}
operator std::chrono::system_clock::time_point() const
{
using internal_duration =
typename std::chrono::system_clock::time_point::duration;
// Normally DST begins at A.M. 3 or 4. If we re-use conversion operator
// of local_date and local_time independently, the conversion fails if
// it is the day when DST begins or ends. Since local_date considers the
// time is 00:00 A.M. and local_time does not consider DST because it
// does not have any date information. We need to consider both date and
// time information at the same time to convert it correctly.
std::tm t;
t.tm_sec = static_cast<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
// std::mktime returns date as local time zone. no conversion needed
auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t));
dt += std::chrono::duration_cast<internal_duration>(
std::chrono::milliseconds(this->time.millisecond) +
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
return dt;
}
operator std::time_t() const
{
return std::chrono::system_clock::to_time_t(
std::chrono::system_clock::time_point(*this));
}
local_datetime() = default;
~local_datetime() = default;
local_datetime(local_datetime const&) = default;
local_datetime(local_datetime&&) = default;
local_datetime& operator=(local_datetime const&) = default;
local_datetime& operator=(local_datetime&&) = default;
};
inline bool operator==(const local_datetime& lhs, const local_datetime& rhs)
{
return std::make_tuple(lhs.date, lhs.time) ==
std::make_tuple(rhs.date, rhs.time);
}
inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const local_datetime& lhs, const local_datetime& rhs)
{
return std::make_tuple(lhs.date, lhs.time) <
std::make_tuple(rhs.date, rhs.time);
}
inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const local_datetime& lhs, const local_datetime& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt)
{
os << dt.date << 'T' << dt.time;
return os;
}
struct offset_datetime
{
local_date date{};
local_time time{};
time_offset offset{};
offset_datetime(local_date d, local_time t, time_offset o)
: date(d), time(t), offset(o)
{}
offset_datetime(const local_datetime& dt, time_offset o)
: date(dt.date), time(dt.time), offset(o)
{}
explicit offset_datetime(const local_datetime& ld)
: date(ld.date), time(ld.time), offset(get_local_offset(nullptr))
// use the current local timezone offset
{}
explicit offset_datetime(const std::chrono::system_clock::time_point& tp)
: offset(0, 0) // use gmtime
{
const auto timet = std::chrono::system_clock::to_time_t(tp);
const auto tm = detail::gmtime_s(&timet);
this->date = local_date(tm);
this->time = local_time(tm);
}
explicit offset_datetime(const std::time_t& t)
: offset(0, 0) // use gmtime
{
const auto tm = detail::gmtime_s(&t);
this->date = local_date(tm);
this->time = local_time(tm);
}
explicit offset_datetime(const std::tm& t)
: offset(0, 0) // assume gmtime
{
this->date = local_date(t);
this->time = local_time(t);
}
operator std::chrono::system_clock::time_point() const
{
// get date-time
using internal_duration =
typename std::chrono::system_clock::time_point::duration;
// first, convert it to local date-time information in the same way as
// local_datetime does. later we will use time_t to adjust time offset.
std::tm t;
t.tm_sec = static_cast<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
const std::time_t tp_loc = std::mktime(std::addressof(t));
auto tp = std::chrono::system_clock::from_time_t(tp_loc);
tp += std::chrono::duration_cast<internal_duration>(
std::chrono::milliseconds(this->time.millisecond) +
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
// Since mktime uses local time zone, it should be corrected.
// `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
// we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
// to add `+09:00` to `03:00:00Z`.
// Here, it uses the time_t converted from date-time info to handle
// daylight saving time.
const auto ofs = get_local_offset(std::addressof(tp_loc));
tp += std::chrono::hours (ofs.hour);
tp += std::chrono::minutes(ofs.minute);
// We got `12:00:00Z` by correcting local timezone applied by mktime.
// Then we will apply the offset. Let's say `12:00:00-08:00` is given.
// And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
// So we need to subtract the offset.
tp -= std::chrono::minutes(this->offset);
return tp;
}
operator std::time_t() const
{
return std::chrono::system_clock::to_time_t(
std::chrono::system_clock::time_point(*this));
}
offset_datetime() = default;
~offset_datetime() = default;
offset_datetime(offset_datetime const&) = default;
offset_datetime(offset_datetime&&) = default;
offset_datetime& operator=(offset_datetime const&) = default;
offset_datetime& operator=(offset_datetime&&) = default;
private:
static time_offset get_local_offset(const std::time_t* tp)
{
// get local timezone with the same date-time information as mktime
const auto t = detail::localtime_s(tp);
std::array<char, 6> buf;
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
if(result != 5)
{
throw std::runtime_error("toml::offset_datetime: cannot obtain "
"timezone information of current env");
}
const int ofs = std::atoi(buf.data());
const int ofs_h = ofs / 100;
const int ofs_m = ofs - (ofs_h * 100);
return time_offset(ofs_h, ofs_m);
}
};
inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs)
{
return std::make_tuple(lhs.date, lhs.time, lhs.offset) ==
std::make_tuple(rhs.date, rhs.time, rhs.offset);
}
inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs)
{
return std::make_tuple(lhs.date, lhs.time, lhs.offset) <
std::make_tuple(rhs.date, rhs.time, rhs.offset);
}
inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const offset_datetime& dt)
{
os << dt.date << 'T' << dt.time << dt.offset;
return os;
}
}//toml
#endif// TOML11_DATETIME
+83
View File
@@ -0,0 +1,83 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP
#include <array>
#include <string>
#include <stdexcept>
#include <cstring>
#include "source_location.hpp"
namespace toml
{
struct file_io_error : public std::runtime_error
{
public:
file_io_error(int errnum, const std::string& msg, const std::string& fname)
: std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)),
errno_(errnum)
{}
int get_errno() const noexcept {return errno_;}
private:
int errno_;
};
struct exception : public std::exception
{
public:
explicit exception(const source_location& loc): loc_(loc) {}
virtual ~exception() noexcept override = default;
virtual const char* what() const noexcept override {return "";}
virtual source_location const& location() const noexcept {return loc_;}
protected:
source_location loc_;
};
struct syntax_error : public toml::exception
{
public:
explicit syntax_error(const std::string& what_arg, const source_location& loc)
: exception(loc), what_(what_arg)
{}
virtual ~syntax_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
protected:
std::string what_;
};
struct type_error : public toml::exception
{
public:
explicit type_error(const std::string& what_arg, const source_location& loc)
: exception(loc), what_(what_arg)
{}
virtual ~type_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
protected:
std::string what_;
};
struct internal_error : public toml::exception
{
public:
explicit internal_error(const std::string& what_arg, const source_location& loc)
: exception(loc), what_(what_arg)
{}
virtual ~internal_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
protected:
std::string what_;
};
} // toml
#endif // TOML_EXCEPTION
+19
View File
@@ -0,0 +1,19 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_FROM_HPP
#define TOML11_FROM_HPP
namespace toml
{
template<typename T>
struct from;
// {
// static T from_toml(const toml::value& v)
// {
// // User-defined conversions ...
// }
// };
} // toml
#endif // TOML11_FROM_HPP
+1154
View File
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_INTO_HPP
#define TOML11_INTO_HPP
namespace toml
{
template<typename T>
struct into;
// {
// static toml::value into_toml(const T& user_defined_type)
// {
// // User-defined conversions ...
// }
// };
} // toml
#endif // TOML11_INTO_HPP
+294
View File
@@ -0,0 +1,294 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_LEXER_HPP
#define TOML11_LEXER_HPP
#include <istream>
#include <sstream>
#include <stdexcept>
#include "combinator.hpp"
namespace toml
{
namespace detail
{
// these scans contents from current location in a container of char
// and extract a region that matches their own pattern.
// to see the implementation of each component, see combinator.hpp.
using lex_wschar = either<character<' '>, character<'\t'>>;
using lex_ws = repeat<lex_wschar, at_least<1>>;
using lex_newline = either<character<'\n'>,
sequence<character<'\r'>, character<'\n'>>>;
using lex_lower = in_range<'a', 'z'>;
using lex_upper = in_range<'A', 'Z'>;
using lex_alpha = either<lex_lower, lex_upper>;
using lex_digit = in_range<'0', '9'>;
using lex_nonzero = in_range<'1', '9'>;
using lex_oct_dig = in_range<'0', '7'>;
using lex_bin_dig = in_range<'0', '1'>;
using lex_hex_dig = either<lex_digit, in_range<'A', 'F'>, in_range<'a', 'f'>>;
using lex_hex_prefix = sequence<character<'0'>, character<'x'>>;
using lex_oct_prefix = sequence<character<'0'>, character<'o'>>;
using lex_bin_prefix = sequence<character<'0'>, character<'b'>>;
using lex_underscore = character<'_'>;
using lex_plus = character<'+'>;
using lex_minus = character<'-'>;
using lex_sign = either<lex_plus, lex_minus>;
// digit | nonzero 1*(digit | _ digit)
using lex_unsigned_dec_int = either<sequence<lex_nonzero, repeat<
either<lex_digit, sequence<lex_underscore, lex_digit>>, at_least<1>>>,
lex_digit>;
// (+|-)? unsigned_dec_int
using lex_dec_int = sequence<maybe<lex_sign>, lex_unsigned_dec_int>;
// hex_prefix hex_dig *(hex_dig | _ hex_dig)
using lex_hex_int = sequence<lex_hex_prefix, sequence<lex_hex_dig, repeat<
either<lex_hex_dig, sequence<lex_underscore, lex_hex_dig>>, unlimited>>>;
// oct_prefix oct_dig *(oct_dig | _ oct_dig)
using lex_oct_int = sequence<lex_oct_prefix, sequence<lex_oct_dig, repeat<
either<lex_oct_dig, sequence<lex_underscore, lex_oct_dig>>, unlimited>>>;
// bin_prefix bin_dig *(bin_dig | _ bin_dig)
using lex_bin_int = sequence<lex_bin_prefix, sequence<lex_bin_dig, repeat<
either<lex_bin_dig, sequence<lex_underscore, lex_bin_dig>>, unlimited>>>;
// (dec_int | hex_int | oct_int | bin_int)
using lex_integer = either<lex_bin_int, lex_oct_int, lex_hex_int, lex_dec_int>;
// ===========================================================================
using lex_inf = sequence<character<'i'>, character<'n'>, character<'f'>>;
using lex_nan = sequence<character<'n'>, character<'a'>, character<'n'>>;
using lex_special_float = sequence<maybe<lex_sign>, either<lex_inf, lex_nan>>;
using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit,
sequence<lex_underscore, lex_digit>>, unlimited>>;
using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>;
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
maybe<lex_sign>, lex_zero_prefixable_int>;
using lex_float = either<lex_special_float,
sequence<lex_dec_int, either<lex_exponent_part,
sequence<lex_fractional_part, maybe<lex_exponent_part>>>>>;
// ===========================================================================
using lex_true = sequence<character<'t'>, character<'r'>,
character<'u'>, character<'e'>>;
using lex_false = sequence<character<'f'>, character<'a'>, character<'l'>,
character<'s'>, character<'e'>>;
using lex_boolean = either<lex_true, lex_false>;
// ===========================================================================
using lex_date_fullyear = repeat<lex_digit, exactly<4>>;
using lex_date_month = repeat<lex_digit, exactly<2>>;
using lex_date_mday = repeat<lex_digit, exactly<2>>;
using lex_time_delim = either<character<'T'>, character<'t'>, character<' '>>;
using lex_time_hour = repeat<lex_digit, exactly<2>>;
using lex_time_minute = repeat<lex_digit, exactly<2>>;
using lex_time_second = repeat<lex_digit, exactly<2>>;
using lex_time_secfrac = sequence<character<'.'>,
repeat<lex_digit, at_least<1>>>;
using lex_time_numoffset = sequence<either<character<'+'>, character<'-'>>,
sequence<lex_time_hour, character<':'>,
lex_time_minute>>;
using lex_time_offset = either<character<'Z'>, character<'z'>,
lex_time_numoffset>;
using lex_partial_time = sequence<lex_time_hour, character<':'>,
lex_time_minute, character<':'>,
lex_time_second, maybe<lex_time_secfrac>>;
using lex_full_date = sequence<lex_date_fullyear, character<'-'>,
lex_date_month, character<'-'>,
lex_date_mday>;
using lex_full_time = sequence<lex_partial_time, lex_time_offset>;
using lex_offset_date_time = sequence<lex_full_date, lex_time_delim, lex_full_time>;
using lex_local_date_time = sequence<lex_full_date, lex_time_delim, lex_partial_time>;
using lex_local_date = lex_full_date;
using lex_local_time = lex_partial_time;
// ===========================================================================
using lex_quotation_mark = character<'"'>;
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) is allowed
in_range<0x0A, 0x1F>,
character<0x22>, character<0x5C>,
character<0x7F>>>;
using lex_escape = character<'\\'>;
using lex_escape_unicode_short = sequence<character<'u'>,
repeat<lex_hex_dig, exactly<4>>>;
using lex_escape_unicode_long = sequence<character<'U'>,
repeat<lex_hex_dig, exactly<8>>>;
using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
character<'b'>, character<'f'>,
character<'n'>, character<'r'>,
character<'t'>,
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
character<'e'>, // ESC (0x1B)
#endif
lex_escape_unicode_short,
lex_escape_unicode_long
>;
using lex_escaped = sequence<lex_escape, lex_escape_seq_char>;
using lex_basic_char = either<lex_basic_unescaped, lex_escaped>;
using lex_basic_string = sequence<lex_quotation_mark,
repeat<lex_basic_char, unlimited>,
lex_quotation_mark>;
// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
// are allowed to be used.
// After this, the following strings are *explicitly* allowed.
// - One or two `"`s in a multi-line basic string is allowed wherever it is.
// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
// - One or two `"`s can appear just before or after the delimiter.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// str7 = """"This," she said, "is just a pointless statement.""""
// ```
// In the current implementation (v3.3.0), it is difficult to parse `str7` in
// the above example. It is difficult to recognize `"` at the end of string body
// collectly. It will be misunderstood as a `"""` delimiter and an additional,
// invalid `"`. Like this:
// ```console
// what(): [error] toml::parse_table: invalid line format
// --> hoge.toml
// |
// 13 | str7 = """"This," she said, "is just a pointless statement.""""
// | ^- expected newline, but got '"'.
// ```
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
// the string body.
//
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
using lex_ml_basic_string_open = lex_ml_basic_string_delim;
using lex_ml_basic_string_close = sequence<
repeat<lex_quotation_mark, exactly<3>>,
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>;
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 is tab
in_range<0x0A, 0x1F>,
character<0x5C>, // backslash
character<0x7F>, // DEL
lex_ml_basic_string_delim>>;
using lex_ml_basic_escaped_newline = sequence<
lex_escape, maybe<lex_ws>, lex_newline,
repeat<either<lex_ws, lex_newline>, unlimited>>;
using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
lex_ml_basic_escaped_newline>,
unlimited>;
using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
lex_ml_basic_body,
lex_ml_basic_string_close>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, in_range<0x0A, 0x1F>,
character<0x7F>, character<0x27>>>;
using lex_apostrophe = character<'\''>;
using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>,
lex_apostrophe>;
// the same reason as above.
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
using lex_ml_literal_string_open = lex_ml_literal_string_delim;
using lex_ml_literal_string_close = sequence<
repeat<lex_apostrophe, exactly<3>>,
maybe<lex_apostrophe>, maybe<lex_apostrophe>
>;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x0A, 0x1F>,
character<0x7F>,
lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
unlimited>;
using lex_ml_literal_string = sequence<lex_ml_literal_string_open,
lex_ml_literal_body,
lex_ml_literal_string_close>;
using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>;
// ===========================================================================
using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>;
using lex_unquoted_key = repeat<either<lex_alpha, lex_digit,
character<'-'>, character<'_'>>,
at_least<1>>;
using lex_quoted_key = either<lex_basic_string, lex_literal_string>;
using lex_simple_key = either<lex_unquoted_key, lex_quoted_key>;
using lex_dotted_key = sequence<lex_simple_key,
repeat<sequence<lex_dot_sep, lex_simple_key>,
at_least<1>
>
>;
using lex_key = either<lex_dotted_key, lex_simple_key>;
using lex_keyval_sep = sequence<maybe<lex_ws>,
character<'='>,
maybe<lex_ws>>;
using lex_std_table_open = character<'['>;
using lex_std_table_close = character<']'>;
using lex_std_table = sequence<lex_std_table_open,
maybe<lex_ws>,
lex_key,
maybe<lex_ws>,
lex_std_table_close>;
using lex_array_table_open = sequence<lex_std_table_open, lex_std_table_open>;
using lex_array_table_close = sequence<lex_std_table_close, lex_std_table_close>;
using lex_array_table = sequence<lex_array_table_open,
maybe<lex_ws>,
lex_key,
maybe<lex_ws>,
lex_array_table_close>;
using lex_utf8_1byte = in_range<0x00, 0x7F>;
using lex_utf8_2byte = sequence<
in_range<'\xC2', '\xDF'>,
in_range<'\x80', '\xBF'>
>;
using lex_utf8_3byte = sequence<either<
sequence<character<'\xE0'>, in_range<'\xA0', '\xBF'>>,
sequence<in_range<'\xE1', '\xEC'>, in_range<'\x80', '\xBF'>>,
sequence<character<'\xED'>, in_range<'\x80', '\x9F'>>,
sequence<in_range<'\xEE', '\xEF'>, in_range<'\x80', '\xBF'>>
>, in_range<'\x80', '\xBF'>>;
using lex_utf8_4byte = sequence<either<
sequence<character<'\xF0'>, in_range<'\x90', '\xBF'>>,
sequence<in_range<'\xF1', '\xF3'>, in_range<'\x80', '\xBF'>>,
sequence<character<'\xF4'>, in_range<'\x80', '\x8F'>>
>, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>;
using lex_utf8_code = either<
lex_utf8_1byte,
lex_utf8_2byte,
lex_utf8_3byte,
lex_utf8_4byte
>;
using lex_comment_start_symbol = character<'#'>;
using lex_non_eol_ascii = either<character<0x09>, in_range<0x20, 0x7E>>;
using lex_comment = sequence<lex_comment_start_symbol, repeat<either<
lex_non_eol_ascii, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte>, unlimited>>;
} // detail
} // toml
#endif // TOML_LEXER_HPP
+113
View File
@@ -0,0 +1,113 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_LITERAL_HPP
#define TOML11_LITERAL_HPP
#include "parser.hpp"
namespace toml
{
inline namespace literals
{
inline namespace toml_literals
{
// implementation
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
literal_internal_impl(::toml::detail::location loc)
{
using value_type = ::toml::basic_value<
TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>;
// if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>,
::toml::detail::maybe<::toml::detail::lex_comment>,
::toml::detail::lex_newline
>, ::toml::detail::at_least<1>>;
skip_line::invoke(loc);
// if there are some whitespaces before a value, skip them.
using skip_ws = ::toml::detail::repeat<
::toml::detail::lex_ws, ::toml::detail::at_least<1>>;
skip_ws::invoke(loc);
// to distinguish arrays and tables, first check it is a table or not.
//
// "[1,2,3]"_toml; // this is an array
// "[table]"_toml; // a table that has an empty table named "table" inside.
// "[[1,2,3]]"_toml; // this is an array of arrays
// "[[table]]"_toml; // this is a table that has an array of tables inside.
//
// "[[1]]"_toml; // this can be both... (currently it becomes a table)
// "1 = [{}]"_toml; // this is a table that has an array of table named 1.
// "[[1,]]"_toml; // this is an array of arrays.
// "[[1],]"_toml; // this also.
const auto the_front = loc.iter();
const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc);
loc.reset(the_front);
const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc);
loc.reset(the_front);
// If it is neither a table-key or a array-of-table-key, it may be a value.
if(!is_table_key && !is_aots_key)
{
if(auto data = ::toml::detail::parse_value<value_type>(loc, 0))
{
return data.unwrap();
}
}
// Note that still it can be a table, because the literal might be something
// like the following.
// ```cpp
// R"( // c++11 raw string literals
// key = "value"
// int = 42
// )"_toml;
// ```
// It is a valid toml file.
// It should be parsed as if we parse a file with this content.
if(auto data = ::toml::detail::parse_toml_file<value_type>(loc))
{
return data.unwrap();
}
else // none of them.
{
throw ::toml::syntax_error(data.unwrap_err(), source_location(loc));
}
}
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(str, str + len));
// literal length does not include the null character at the end.
return literal_internal_impl(std::move(loc));
}
// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers.
// So here we use the feature test macro for `char8_t` itself.
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
// value of u8"" literal has been changed from char to char8_t and char8_t is
// NOT compatible to char
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char8_t* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(reinterpret_cast<const char*>(str),
reinterpret_cast<const char*>(str) + len));
return literal_internal_impl(std::move(loc));
}
#endif
} // toml_literals
} // literals
} // toml
#endif//TOML11_LITERAL_HPP
+121
View File
@@ -0,0 +1,121 @@
#ifndef TOML11_MACROS_HPP
#define TOML11_MACROS_HPP
#define TOML11_STRINGIZE_AUX(x) #x
#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x)
#define TOML11_CONCATENATE_AUX(x, y) x##y
#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y)
// ============================================================================
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
// ----------------------------------------------------------------------------
// TOML11_ARGS_SIZE
#define TOML11_INDEX_RSEQ() \
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define TOML11_ARGS_SIZE_IMPL(\
ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \
ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \
ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \
ARG31, ARG32, N, ...) N
#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__)
#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ())
// ----------------------------------------------------------------------------
// TOML11_FOR_EACH_VA_ARGS
#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1)
#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\
TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__)
// ----------------------------------------------------------------------------
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
// use it in the following way.
// ```cpp
// namespace foo
// {
// struct Foo
// {
// std::string s;
// double d;
// int i;
// };
// } // foo
//
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
// ```
// And then you can use `toml::find<foo::Foo>(file, "foo");`
//
#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
obj.VAR_NAME = toml::find<decltype(obj.VAR_NAME)>(v, TOML11_STRINGIZE(VAR_NAME));
#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME;
#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
namespace toml { \
template<> \
struct from<NAME> \
{ \
template<typename C, template<typename ...> class T, \
template<typename ...> class A> \
static NAME from_toml(const basic_value<C, T, A>& v) \
{ \
NAME obj; \
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
return obj; \
} \
}; \
template<> \
struct into<NAME> \
{ \
static value into_toml(const NAME& obj) \
{ \
::toml::value v = ::toml::table{}; \
TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \
return v; \
} \
}; \
} /* toml */
#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
#endif// TOML11_MACROS_HPP
+2611
View File
File diff suppressed because it is too large Load Diff
+416
View File
@@ -0,0 +1,416 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_REGION_HPP
#define TOML11_REGION_HPP
#include <memory>
#include <vector>
#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <iomanip>
#include <cassert>
#include "color.hpp"
namespace toml
{
namespace detail
{
// helper function to avoid std::string(0, 'c') or std::string(iter, iter)
template<typename Iterator>
std::string make_string(Iterator first, Iterator last)
{
if(first == last) {return "";}
return std::string(first, last);
}
inline std::string make_string(std::size_t len, char c)
{
if(len == 0) {return "";}
return std::string(len, c);
}
// region_base is a base class of location and region that are defined below.
// it will be used to generate better error messages.
struct region_base
{
region_base() = default;
virtual ~region_base() = default;
region_base(const region_base&) = default;
region_base(region_base&& ) = default;
region_base& operator=(const region_base&) = default;
region_base& operator=(region_base&& ) = default;
virtual bool is_ok() const noexcept {return false;}
virtual char front() const noexcept {return '\0';}
virtual std::string str() const {return std::string("unknown region");}
virtual std::string name() const {return std::string("unknown file");}
virtual std::string line() const {return std::string("unknown line");}
virtual std::string line_num() const {return std::string("?");}
// length of the region
virtual std::size_t size() const noexcept {return 0;}
// number of characters in the line before the region
virtual std::size_t before() const noexcept {return 0;}
// number of characters in the line after the region
virtual std::size_t after() const noexcept {return 0;}
virtual std::vector<std::string> comments() const {return {};}
// ```toml
// # comment_before
// key = "value" # comment_inline
// ```
};
// location represents a position in a container, which contains a file content.
// it can be considered as a region that contains only one character.
//
// it contains pointer to the file content and iterator that points the current
// location.
struct location final : public region_base
{
using const_iterator = typename std::vector<char>::const_iterator;
using difference_type = typename std::iterator_traits<const_iterator>::difference_type;
using source_ptr = std::shared_ptr<const std::vector<char>>;
location(std::string source_name, std::vector<char> cont)
: source_(std::make_shared<std::vector<char>>(std::move(cont))),
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
{}
location(std::string source_name, const std::string& cont)
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
{}
location(const location&) = default;
location(location&&) = default;
location& operator=(const location&) = default;
location& operator=(location&&) = default;
~location() = default;
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *iter_;}
// this const prohibits codes like `++(loc.iter())`.
std::add_const<const_iterator>::type iter() const noexcept {return iter_;}
const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();}
// XXX `location::line_num()` used to be implemented using `std::count` to
// count a number of '\n'. But with a long toml file (typically, 10k lines),
// it becomes intolerably slow because each time it generates error messages,
// it counts '\n' from thousands of characters. To workaround it, I decided
// to introduce `location::line_number_` member variable and synchronize it
// to the location changes the point to look. So an overload of `iter()`
// which returns mutable reference is removed and `advance()`, `retrace()`
// and `reset()` is added.
void advance(difference_type n = 1) noexcept
{
this->line_number_ += static_cast<std::size_t>(
std::count(this->iter_, std::next(this->iter_, n), '\n'));
this->iter_ += n;
return;
}
void retrace(difference_type n = 1) noexcept
{
this->line_number_ -= static_cast<std::size_t>(
std::count(std::prev(this->iter_, n), this->iter_, '\n'));
this->iter_ -= n;
return;
}
void reset(const_iterator rollback) noexcept
{
// since c++11, std::distance works in both ways for random-access
// iterators and returns a negative value if `first > last`.
if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
{
this->line_number_ -= static_cast<std::size_t>(
std::count(rollback, this->iter_, '\n'));
}
else // iter < rollback [[unlikely]]
{
this->line_number_ += static_cast<std::size_t>(
std::count(this->iter_, rollback, '\n'));
}
this->iter_ = rollback;
return;
}
std::string str() const override {return make_string(1, *this->iter());}
std::string name() const override {return source_name_;}
std::string line_num() const override
{
return std::to_string(this->line_number_);
}
std::string line() const override
{
return make_string(this->line_begin(), this->line_end());
}
const_iterator line_begin() const noexcept
{
using reverse_iterator = std::reverse_iterator<const_iterator>;
return std::find(reverse_iterator(this->iter()),
reverse_iterator(this->begin()), '\n').base();
}
const_iterator line_end() const noexcept
{
return std::find(this->iter(), this->end(), '\n');
}
// location is always points a character. so the size is 1.
std::size_t size() const noexcept override
{
return 1u;
}
std::size_t before() const noexcept override
{
const auto sz = std::distance(this->line_begin(), this->iter());
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
std::size_t after() const noexcept override
{
const auto sz = std::distance(this->iter(), this->line_end());
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
source_ptr const& source() const& noexcept {return source_;}
source_ptr&& source() && noexcept {return std::move(source_);}
private:
source_ptr source_;
std::size_t line_number_;
std::string source_name_;
const_iterator iter_;
};
// region represents a range in a container, which contains a file content.
//
// it contains pointer to the file content and iterator that points the first
// and last location.
struct region final : public region_base
{
using const_iterator = typename std::vector<char>::const_iterator;
using source_ptr = std::shared_ptr<const std::vector<char>>;
// delete default constructor. source_ never be null.
region() = delete;
explicit region(const location& loc)
: source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter())
{}
explicit region(location&& loc)
: source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter())
{}
region(const location& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{}
region(location&& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{}
region(const region&) = default;
region(region&&) = default;
region& operator=(const region&) = default;
region& operator=(region&&) = default;
~region() = default;
region& operator+=(const region& other)
{
// different regions cannot be concatenated
assert(this->source_ == other.source_ && this->last_ == other.first_);
this->last_ = other.last_;
return *this;
}
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *first_;}
std::string str() const override {return make_string(first_, last_);}
std::string line() const override
{
if(this->contain_newline())
{
return make_string(this->line_begin(),
std::find(this->line_begin(), this->last(), '\n'));
}
return make_string(this->line_begin(), this->line_end());
}
std::string line_num() const override
{
return std::to_string(1 + std::count(this->begin(), this->first(), '\n'));
}
std::size_t size() const noexcept override
{
const auto sz = std::distance(first_, last_);
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
std::size_t before() const noexcept override
{
const auto sz = std::distance(this->line_begin(), this->first());
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
std::size_t after() const noexcept override
{
const auto sz = std::distance(this->last(), this->line_end());
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
bool contain_newline() const noexcept
{
return std::find(this->first(), this->last(), '\n') != this->last();
}
const_iterator line_begin() const noexcept
{
using reverse_iterator = std::reverse_iterator<const_iterator>;
return std::find(reverse_iterator(this->first()),
reverse_iterator(this->begin()), '\n').base();
}
const_iterator line_end() const noexcept
{
return std::find(this->last(), this->end(), '\n');
}
const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();}
const_iterator first() const noexcept {return first_;}
const_iterator last() const noexcept {return last_;}
source_ptr const& source() const& noexcept {return source_;}
source_ptr&& source() && noexcept {return std::move(source_);}
std::string name() const override {return source_name_;}
std::vector<std::string> comments() const override
{
// assuming the current region (`*this`) points a value.
// ```toml
// a = "value"
// ^^^^^^^- this region
// ```
using rev_iter = std::reverse_iterator<const_iterator>;
std::vector<std::string> com{};
{
// find comments just before the current region.
// ```toml
// # this should be collected.
// # this also.
// a = value # not this.
// ```
// # this is a comment for `a`, not array elements.
// a = [1, 2, 3, 4, 5]
if(this->first() == std::find_if(this->line_begin(), this->first(),
[](const char c) noexcept -> bool {return c == '[' || c == '{';}))
{
auto iter = this->line_begin(); // points the first character
while(iter != this->begin())
{
iter = std::prev(iter);
// range [line_start, iter) represents the previous line
const auto line_start = std::find(
rev_iter(iter), rev_iter(this->begin()), '\n').base();
const auto comment_found = std::find(line_start, iter, '#');
if(comment_found == iter)
{
break; // comment not found.
}
// exclude the following case.
// > a = "foo" # comment // <-- this is not a comment for b but a.
// > b = "current value"
if(std::all_of(line_start, comment_found,
[](const char c) noexcept -> bool {
return c == ' ' || c == '\t';
}))
{
// unwrap the first '#' by std::next.
auto s = make_string(std::next(comment_found), iter);
if(!s.empty() && s.back() == '\r') {s.pop_back();}
com.push_back(std::move(s));
}
else
{
break;
}
iter = line_start;
}
}
}
if(com.size() > 1)
{
std::reverse(com.begin(), com.end());
}
{
// find comments just after the current region.
// ```toml
// # not this.
// a = value # this one.
// a = [ # not this (technically difficult)
//
// ] # and this.
// ```
// The reason why it's difficult is that it requires parsing in the
// following case.
// ```toml
// a = [ 10 # this comment is for `10`. not for `a` but `a[0]`.
// # ...
// ] # this is apparently a comment for a.
//
// b = [
// 3.14 ] # there is no way to add a comment to `3.14` currently.
//
// c = [
// 3.14 # do this if you need a comment here.
// ]
// ```
const auto comment_found =
std::find(this->last(), this->line_end(), '#');
if(comment_found != this->line_end()) // '#' found
{
// table = {key = "value"} # what is this for?
// the above comment is not for "value", but {key="value"}.
if(comment_found == std::find_if(this->last(), comment_found,
[](const char c) noexcept -> bool {
return !(c == ' ' || c == '\t' || c == ',');
}))
{
// unwrap the first '#' by std::next.
auto s = make_string(std::next(comment_found), this->line_end());
if(!s.empty() && s.back() == '\r') {s.pop_back();}
com.push_back(std::move(s));
}
}
}
return com;
}
private:
source_ptr source_;
std::string source_name_;
const_iterator first_, last_;
};
} // detail
} // toml
#endif// TOML11_REGION_H
+717
View File
@@ -0,0 +1,717 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_RESULT_HPP
#define TOML11_RESULT_HPP
#include "traits.hpp"
#include <type_traits>
#include <stdexcept>
#include <utility>
#include <new>
#include <string>
#include <sstream>
#include <cassert>
namespace toml
{
template<typename T>
struct success
{
using value_type = T;
value_type value;
explicit success(const value_type& v)
noexcept(std::is_nothrow_copy_constructible<value_type>::value)
: value(v)
{}
explicit success(value_type&& v)
noexcept(std::is_nothrow_move_constructible<value_type>::value)
: value(std::move(v))
{}
template<typename U>
explicit success(U&& v): value(std::forward<U>(v)) {}
template<typename U>
explicit success(const success<U>& v): value(v.value) {}
template<typename U>
explicit success(success<U>&& v): value(std::move(v.value)) {}
~success() = default;
success(const success&) = default;
success(success&&) = default;
success& operator=(const success&) = default;
success& operator=(success&&) = default;
};
template<typename T>
struct failure
{
using value_type = T;
value_type value;
explicit failure(const value_type& v)
noexcept(std::is_nothrow_copy_constructible<value_type>::value)
: value(v)
{}
explicit failure(value_type&& v)
noexcept(std::is_nothrow_move_constructible<value_type>::value)
: value(std::move(v))
{}
template<typename U>
explicit failure(U&& v): value(std::forward<U>(v)) {}
template<typename U>
explicit failure(const failure<U>& v): value(v.value) {}
template<typename U>
explicit failure(failure<U>&& v): value(std::move(v.value)) {}
~failure() = default;
failure(const failure&) = default;
failure(failure&&) = default;
failure& operator=(const failure&) = default;
failure& operator=(failure&&) = default;
};
template<typename T>
success<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
ok(T&& v)
{
return success<
typename std::remove_cv<typename std::remove_reference<T>::type>::type
>(std::forward<T>(v));
}
template<typename T>
failure<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
err(T&& v)
{
return failure<
typename std::remove_cv<typename std::remove_reference<T>::type>::type
>(std::forward<T>(v));
}
inline success<std::string> ok(const char* literal)
{
return success<std::string>(std::string(literal));
}
inline failure<std::string> err(const char* literal)
{
return failure<std::string>(std::string(literal));
}
template<typename T, typename E>
struct result
{
using value_type = T;
using error_type = E;
using success_type = success<value_type>;
using failure_type = failure<error_type>;
result(const success_type& s): is_ok_(true)
{
auto tmp = ::new(std::addressof(this->succ)) success_type(s);
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
result(const failure_type& f): is_ok_(false)
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
result(success_type&& s): is_ok_(true)
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
result(failure_type&& f): is_ok_(false)
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
template<typename U>
result(const success<U>& s): is_ok_(true)
{
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
template<typename U>
result(const failure<U>& f): is_ok_(false)
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
template<typename U>
result(success<U>&& s): is_ok_(true)
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
template<typename U>
result(failure<U>&& f): is_ok_(false)
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
result& operator=(const success_type& s)
{
this->cleanup();
this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(s);
assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this;
}
result& operator=(const failure_type& f)
{
this->cleanup();
this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this;
}
result& operator=(success_type&& s)
{
this->cleanup();
this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this;
}
result& operator=(failure_type&& f)
{
this->cleanup();
this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this;
}
template<typename U>
result& operator=(const success<U>& s)
{
this->cleanup();
this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this;
}
template<typename U>
result& operator=(const failure<U>& f)
{
this->cleanup();
this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this;
}
template<typename U>
result& operator=(success<U>&& s)
{
this->cleanup();
this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this;
}
template<typename U>
result& operator=(failure<U>&& f)
{
this->cleanup();
this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this;
}
~result() noexcept {this->cleanup();}
result(const result& other): is_ok_(other.is_ok())
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
}
result(result&& other): is_ok_(other.is_ok())
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
}
template<typename U, typename F>
result(const result<U, F>& other): is_ok_(other.is_ok())
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
}
template<typename U, typename F>
result(result<U, F>&& other): is_ok_(other.is_ok())
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
}
result& operator=(const result& other)
{
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
is_ok_ = other.is_ok();
return *this;
}
result& operator=(result&& other)
{
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
is_ok_ = other.is_ok();
return *this;
}
template<typename U, typename F>
result& operator=(const result<U, F>& other)
{
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
is_ok_ = other.is_ok();
return *this;
}
template<typename U, typename F>
result& operator=(result<U, F>&& other)
{
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
is_ok_ = other.is_ok();
return *this;
}
bool is_ok() const noexcept {return is_ok_;}
bool is_err() const noexcept {return !is_ok_;}
operator bool() const noexcept {return is_ok_;}
value_type& unwrap() &
{
if(is_err())
{
throw std::runtime_error("toml::result: bad unwrap: " +
format_error(this->as_err()));
}
return this->succ.value;
}
value_type const& unwrap() const&
{
if(is_err())
{
throw std::runtime_error("toml::result: bad unwrap: " +
format_error(this->as_err()));
}
return this->succ.value;
}
value_type&& unwrap() &&
{
if(is_err())
{
throw std::runtime_error("toml::result: bad unwrap: " +
format_error(this->as_err()));
}
return std::move(this->succ.value);
}
value_type& unwrap_or(value_type& opt) &
{
if(is_err()) {return opt;}
return this->succ.value;
}
value_type const& unwrap_or(value_type const& opt) const&
{
if(is_err()) {return opt;}
return this->succ.value;
}
value_type unwrap_or(value_type opt) &&
{
if(is_err()) {return opt;}
return this->succ.value;
}
error_type& unwrap_err() &
{
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
return this->fail.value;
}
error_type const& unwrap_err() const&
{
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
return this->fail.value;
}
error_type&& unwrap_err() &&
{
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
return std::move(this->fail.value);
}
value_type& as_ok() & noexcept {return this->succ.value;}
value_type const& as_ok() const& noexcept {return this->succ.value;}
value_type&& as_ok() && noexcept {return std::move(this->succ.value);}
error_type& as_err() & noexcept {return this->fail.value;}
error_type const& as_err() const& noexcept {return this->fail.value;}
error_type&& as_err() && noexcept {return std::move(this->fail.value);}
// prerequisities
// F: T -> U
// retval: result<U, E>
template<typename F>
result<detail::return_type_of_t<F, value_type&>, error_type>
map(F&& f) &
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<detail::return_type_of_t<F, value_type const&>, error_type>
map(F&& f) const&
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<detail::return_type_of_t<F, value_type &&>, error_type>
map(F&& f) &&
{
if(this->is_ok()){return ok(f(std::move(this->as_ok())));}
return err(std::move(this->as_err()));
}
// prerequisities
// F: E -> F
// retval: result<T, F>
template<typename F>
result<value_type, detail::return_type_of_t<F, error_type&>>
map_err(F&& f) &
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, detail::return_type_of_t<F, error_type const&>>
map_err(F&& f) const&
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, detail::return_type_of_t<F, error_type&&>>
map_err(F&& f) &&
{
if(this->is_err()){return err(f(std::move(this->as_err())));}
return ok(std::move(this->as_ok()));
}
// prerequisities
// F: T -> U
// retval: U
template<typename F, typename U>
detail::return_type_of_t<F, value_type&>
map_or_else(F&& f, U&& opt) &
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
detail::return_type_of_t<F, value_type const&>
map_or_else(F&& f, U&& opt) const&
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
detail::return_type_of_t<F, value_type&&>
map_or_else(F&& f, U&& opt) &&
{
if(this->is_err()){return std::forward<U>(opt);}
return f(std::move(this->as_ok()));
}
// prerequisities
// F: E -> U
// retval: U
template<typename F, typename U>
detail::return_type_of_t<F, error_type&>
map_err_or_else(F&& f, U&& opt) &
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(this->as_err());
}
template<typename F, typename U>
detail::return_type_of_t<F, error_type const&>
map_err_or_else(F&& f, U&& opt) const&
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(this->as_err());
}
template<typename F, typename U>
detail::return_type_of_t<F, error_type&&>
map_err_or_else(F&& f, U&& opt) &&
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(std::move(this->as_err()));
}
// prerequisities:
// F: func T -> U
// toml::err(error_type) should be convertible to U.
// normally, type U is another result<S, F> and E is convertible to F
template<typename F>
detail::return_type_of_t<F, value_type&>
and_then(F&& f) &
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
detail::return_type_of_t<F, value_type const&>
and_then(F&& f) const&
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
detail::return_type_of_t<F, value_type&&>
and_then(F&& f) &&
{
if(this->is_ok()){return f(std::move(this->as_ok()));}
return err(std::move(this->as_err()));
}
// prerequisities:
// F: func E -> U
// toml::ok(value_type) should be convertible to U.
// normally, type U is another result<S, F> and T is convertible to S
template<typename F>
detail::return_type_of_t<F, error_type&>
or_else(F&& f) &
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
detail::return_type_of_t<F, error_type const&>
or_else(F&& f) const&
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
detail::return_type_of_t<F, error_type&&>
or_else(F&& f) &&
{
if(this->is_err()){return f(std::move(this->as_err()));}
return ok(std::move(this->as_ok()));
}
// if *this is error, returns *this. otherwise, returns other.
result and_other(const result& other) const&
{
return this->is_err() ? *this : other;
}
result and_other(result&& other) &&
{
return this->is_err() ? std::move(*this) : std::move(other);
}
// if *this is okay, returns *this. otherwise, returns other.
result or_other(const result& other) const&
{
return this->is_ok() ? *this : other;
}
result or_other(result&& other) &&
{
return this->is_ok() ? std::move(*this) : std::move(other);
}
void swap(result<T, E>& other)
{
result<T, E> tmp(std::move(*this));
*this = std::move(other);
other = std::move(tmp);
return ;
}
private:
static std::string format_error(std::exception const& excpt)
{
return std::string(excpt.what());
}
template<typename U, typename std::enable_if<!std::is_base_of<
std::exception, U>::value, std::nullptr_t>::type = nullptr>
static std::string format_error(U const& others)
{
std::ostringstream oss; oss << others;
return oss.str();
}
void cleanup() noexcept
{
if(this->is_ok_) {this->succ.~success_type();}
else {this->fail.~failure_type();}
return;
}
private:
bool is_ok_;
union
{
success_type succ;
failure_type fail;
};
};
template<typename T, typename E>
void swap(result<T, E>& lhs, result<T, E>& rhs)
{
lhs.swap(rhs);
return;
}
// this might be confusing because it eagerly evaluated, while in the other
// cases operator && and || are short-circuited.
//
// template<typename T, typename E>
// inline result<T, E>
// operator&&(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
// {
// return lhs.is_ok() ? rhs : lhs;
// }
//
// template<typename T, typename E>
// inline result<T, E>
// operator||(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
// {
// return lhs.is_ok() ? lhs : rhs;
// }
// ----------------------------------------------------------------------------
// re-use result<T, E> as a optional<T> with none_t
namespace detail
{
struct none_t {};
inline bool operator==(const none_t&, const none_t&) noexcept {return true;}
inline bool operator!=(const none_t&, const none_t&) noexcept {return false;}
inline bool operator< (const none_t&, const none_t&) noexcept {return false;}
inline bool operator<=(const none_t&, const none_t&) noexcept {return true;}
inline bool operator> (const none_t&, const none_t&) noexcept {return false;}
inline bool operator>=(const none_t&, const none_t&) noexcept {return true;}
template<typename charT, typename traitsT>
std::basic_ostream<charT, traitsT>&
operator<<(std::basic_ostream<charT, traitsT>& os, const none_t&)
{
os << "none";
return os;
}
inline failure<none_t> none() noexcept {return failure<none_t>{none_t{}};}
} // detail
} // toml11
#endif// TOML11_RESULT_H
+994
View File
@@ -0,0 +1,994 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_SERIALIZER_HPP
#define TOML11_SERIALIZER_HPP
#include <cmath>
#include <cstdio>
#include <limits>
#if defined(_WIN32)
#include <locale.h>
#elif defined(__APPLE__) || defined(__FreeBSD__)
#include <xlocale.h>
#elif defined(__linux__)
#include <locale.h>
#endif
#include "lexer.hpp"
#include "value.hpp"
namespace toml
{
// This function serialize a key. It checks a string is a bare key and
// escapes special characters if the string is not compatible to a bare key.
// ```cpp
// std::string k("non.bare.key"); // the key itself includes `.`s.
// std::string formatted = toml::format_key(k);
// assert(formatted == "\"non.bare.key\"");
// ```
//
// This function is exposed to make it easy to write a user-defined serializer.
// Since toml restricts characters available in a bare key, generally a string
// should be escaped. But checking whether a string needs to be surrounded by
// a `"` and escaping some special character is boring.
template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_key(const std::basic_string<charT, traits, Alloc>& k)
{
if(k.empty())
{
return std::string("\"\"");
}
// check the key can be a bare (unquoted) key
detail::location loc(k, std::vector<char>(k.begin(), k.end()));
detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end())
{
return k; // all the tokens are consumed. the key is unquoted-key.
}
//if it includes special characters, then format it in a "quoted" key.
std::basic_string<charT, traits, Alloc> serialized("\"");
for(const char c : k)
{
switch(c)
{
case '\\': {serialized += "\\\\"; break;}
case '\"': {serialized += "\\\""; break;}
case '\b': {serialized += "\\b"; break;}
case '\t': {serialized += "\\t"; break;}
case '\f': {serialized += "\\f"; break;}
case '\n': {serialized += "\\n"; break;}
case '\r': {serialized += "\\r"; break;}
default: {
if (c >= 0x00 && c < 0x20)
{
std::array<char, 7> buf;
std::snprintf(buf.data(), buf.size(), "\\u00%02x", static_cast<int>(c));
serialized += buf.data();
}
else
{
serialized += c;
}
break;
}
}
}
serialized += "\"";
return serialized;
}
template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys)
{
if(keys.empty())
{
return std::string("\"\"");
}
std::basic_string<charT, traits, Alloc> serialized;
for(const auto& ky : keys)
{
serialized += format_key(ky);
serialized += charT('.');
}
serialized.pop_back(); // remove the last dot '.'
return serialized;
}
template<typename Value>
struct serializer
{
static_assert(detail::is_basic_value<Value>::value,
"toml::serializer is for toml::value and its variants, "
"toml::basic_value<...>.");
using value_type = Value;
using key_type = typename value_type::key_type ;
using comment_type = typename value_type::comment_type ;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
using floating_type = typename value_type::floating_type ;
using string_type = typename value_type::string_type ;
using local_time_type = typename value_type::local_time_type ;
using local_date_type = typename value_type::local_date_type ;
using local_datetime_type = typename value_type::local_datetime_type ;
using offset_datetime_type = typename value_type::offset_datetime_type;
using array_type = typename value_type::array_type ;
using table_type = typename value_type::table_type ;
serializer(const std::size_t w = 80u,
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
const bool can_be_inlined = false,
const bool no_comment = false,
std::vector<toml::key> ks = {},
const bool value_has_comment = false)
: can_be_inlined_(can_be_inlined), no_comment_(no_comment),
value_has_comment_(value_has_comment && !no_comment),
float_prec_(float_prec), width_(w), keys_(std::move(ks))
{}
~serializer() = default;
std::string operator()(const boolean_type& b) const
{
return b ? "true" : "false";
}
std::string operator()(const integer_type i) const
{
#if defined(_WIN32)
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
setlocale(LC_NUMERIC, "C");
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
locale_t original_locale(0);
if(c_locale != locale_t(0))
{
original_locale = uselocale(c_locale);
}
#endif
const auto str = std::to_string(i);
#if defined(_WIN32)
setlocale(LC_NUMERIC, original_locale.c_str());
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
if(original_locale != locale_t(0))
{
uselocale(original_locale);
}
#endif
return str;
}
std::string operator()(const floating_type f) const
{
if(std::isnan(f))
{
if(std::signbit(f))
{
return std::string("-nan");
}
else
{
return std::string("nan");
}
}
else if(!std::isfinite(f))
{
if(std::signbit(f))
{
return std::string("-inf");
}
else
{
return std::string("inf");
}
}
// set locale to "C".
// To make it thread-local, we use OS-specific features.
// If we set process-global locale, it can break other thread that also
// outputs something simultaneously.
#if defined(_WIN32)
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
setlocale(LC_NUMERIC, "C");
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
locale_t original_locale(0);
if(c_locale != locale_t(0))
{
original_locale = uselocale(c_locale);
}
#endif
const auto fmt = "%.*g";
const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f);
// +1 for null character(\0)
std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0');
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
// restore the original locale
#if defined(_WIN32)
setlocale(LC_NUMERIC, original_locale.c_str());
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
if(original_locale != locale_t(0))
{
uselocale(original_locale);
}
#endif
std::string token(buf.begin(), std::prev(buf.end()));
if(!token.empty() && token.back() == '.') // 1. => 1.0
{
token += '0';
}
const auto e = std::find_if(
token.cbegin(), token.cend(), [](const char c) noexcept -> bool {
return c == 'e' || c == 'E';
});
const auto has_exponent = (token.cend() != e);
const auto has_fraction = (token.cend() != std::find(
token.cbegin(), token.cend(), '.'));
if(!has_exponent && !has_fraction)
{
// the resulting value does not have any float specific part!
token += ".0";
}
return token;
}
std::string operator()(const string_type& s) const
{
if(s.kind == string_t::basic)
{
if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) &&
this->width_ != (std::numeric_limits<std::size_t>::max)())
{
// if linefeed or double-quote is contained,
// make it multiline basic string.
const auto escaped = this->escape_ml_basic_string(s.str);
std::string open("\"\"\"");
std::string close("\"\"\"");
if(escaped.find('\n') != std::string::npos ||
this->width_ < escaped.size() + 6)
{
// if the string body contains newline or is enough long,
// add newlines after and before delimiters.
open += "\n";
close = std::string("\\\n") + close;
}
return open + escaped + close;
}
// no linefeed. try to make it oneline-string.
std::string oneline = this->escape_basic_string(s.str);
if(oneline.size() + 2 < width_ || width_ < 2)
{
const std::string quote("\"");
return quote + oneline + quote;
}
// the line is too long compared to the specified width.
// split it into multiple lines.
std::string token("\"\"\"\n");
while(!oneline.empty())
{
if(oneline.size() < width_)
{
token += oneline;
oneline.clear();
}
else if(oneline.at(width_-2) == '\\')
{
token += oneline.substr(0, width_-2);
token += "\\\n";
oneline.erase(0, width_-2);
}
else
{
token += oneline.substr(0, width_-1);
token += "\\\n";
oneline.erase(0, width_-1);
}
}
return token + std::string("\\\n\"\"\"");
}
else // the string `s` is literal-string.
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{
std::string open("'''");
if(this->width_ + 6 < s.str.size())
{
open += '\n'; // the first newline is ignored by TOML spec
}
const std::string close("'''");
return open + s.str + close;
}
else
{
const std::string quote("'");
return quote + s.str + quote;
}
}
}
std::string operator()(const local_date_type& d) const
{
std::ostringstream oss;
oss << d;
return oss.str();
}
std::string operator()(const local_time_type& t) const
{
std::ostringstream oss;
oss << t;
return oss.str();
}
std::string operator()(const local_datetime_type& dt) const
{
std::ostringstream oss;
oss << dt;
return oss.str();
}
std::string operator()(const offset_datetime_type& odt) const
{
std::ostringstream oss;
oss << odt;
return oss.str();
}
std::string operator()(const array_type& v) const
{
if(v.empty())
{
return std::string("[]");
}
if(this->is_array_of_tables(v))
{
return make_array_of_tables(v);
}
// not an array of tables. normal array.
// first, try to make it inline if none of the elements have a comment.
if( ! this->has_comment_inside(v))
{
const auto inl = this->make_inline_array(v);
if(inl.size() < this->width_ &&
std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend())
{
return inl;
}
}
// if the length exceeds this->width_, print multiline array.
// key = [
// # ...
// 42,
// ...
// ]
std::string token;
std::string current_line;
token += "[\n";
for(const auto& item : v)
{
if( ! item.comments().empty() && !no_comment_)
{
// if comment exists, the element must be the only element in the line.
// e.g. the following is not allowed.
// ```toml
// array = [
// # comment for what?
// 1, 2, 3, 4, 5
// ]
// ```
if(!current_line.empty())
{
if(current_line.back() != '\n')
{
current_line += '\n';
}
token += current_line;
current_line.clear();
}
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
token += toml::visit(*this, item);
if(!token.empty() && token.back() == '\n') {token.pop_back();}
token += ",\n";
continue;
}
std::string next_elem;
if(item.is_table())
{
serializer ser(*this);
ser.can_be_inlined_ = true;
ser.width_ = (std::numeric_limits<std::size_t>::max)();
next_elem += toml::visit(ser, item);
}
else
{
next_elem += toml::visit(*this, item);
}
// comma before newline.
if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();}
// if current line does not exceeds the width limit, continue.
if(current_line.size() + next_elem.size() + 1 < this->width_)
{
current_line += next_elem;
current_line += ',';
}
else if(current_line.empty())
{
// if current line was empty, force put the next_elem because
// next_elem is not splittable
token += next_elem;
token += ",\n";
// current_line is kept empty
}
else // reset current_line
{
assert(current_line.back() == ',');
token += current_line;
token += '\n';
current_line = next_elem;
current_line += ',';
}
}
if(!current_line.empty())
{
if(!current_line.empty() && current_line.back() != '\n')
{
current_line += '\n';
}
token += current_line;
}
token += "]\n";
return token;
}
// templatize for any table-like container
std::string operator()(const table_type& v) const
{
// if an element has a comment, then it can't be inlined.
// table = {# how can we write a comment for this? key = "value"}
if(this->can_be_inlined_ && !(this->has_comment_inside(v)))
{
std::string token;
if(!this->keys_.empty())
{
token += format_key(this->keys_.back());
token += " = ";
}
token += this->make_inline_table(v);
if(token.size() < this->width_ &&
token.end() == std::find(token.begin(), token.end(), '\n'))
{
return token;
}
}
std::string token;
if(!keys_.empty())
{
token += '[';
token += format_keys(keys_);
token += "]\n";
}
token += this->make_multiline_table(v);
return token;
}
private:
std::string escape_basic_string(const std::string& s) const
{
//XXX assuming `s` is a valid utf-8 sequence.
std::string retval;
for(const char c : s)
{
switch(c)
{
case '\\': {retval += "\\\\"; break;}
case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\\n"; break;}
case '\r': {retval += "\\r"; break;}
default :
{
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
}
}
return retval;
}
std::string escape_ml_basic_string(const std::string& s) const
{
std::string retval;
for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i)
{
switch(*i)
{
case '\\': {retval += "\\\\"; break;}
// One or two consecutive "s are allowed.
// Later we will check there are no three consecutive "s.
// case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\n"; break;}
case '\r':
{
if(std::next(i) != e && *std::next(i) == '\n')
{
retval += "\r\n";
++i;
}
else
{
retval += "\\r";
}
break;
}
default :
{
const auto c = *i;
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
}
}
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
// 3 consecutive `"`s are considered as a closing delimiter.
// We need to check if there are 3 or more consecutive `"`s and insert
// backslash to break them down into several short `"`s like the `str6`
// in the following example.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// # str5 = """Here are three quotation marks: """.""" # INVALID
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// ```
auto found_3_quotes = retval.find("\"\"\"");
while(found_3_quotes != std::string::npos)
{
retval.replace(found_3_quotes, 3, "\"\"\\\"");
found_3_quotes = retval.find("\"\"\"");
}
return retval;
}
// if an element of a table or an array has a comment, it cannot be inlined.
bool has_comment_inside(const array_type& a) const noexcept
{
// if no_comment is set, comments would not be written.
if(this->no_comment_) {return false;}
for(const auto& v : a)
{
if(!v.comments().empty()) {return true;}
}
return false;
}
bool has_comment_inside(const table_type& t) const noexcept
{
// if no_comment is set, comments would not be written.
if(this->no_comment_) {return false;}
for(const auto& kv : t)
{
if(!kv.second.comments().empty()) {return true;}
}
return false;
}
std::string make_inline_array(const array_type& v) const
{
assert(!has_comment_inside(v));
std::string token;
token += '[';
bool is_first = true;
for(const auto& item : v)
{
if(is_first) {is_first = false;} else {token += ',';}
token += visit(serializer(
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !item.comments().empty()), item);
}
token += ']';
return token;
}
std::string make_inline_table(const table_type& v) const
{
assert(!has_comment_inside(v));
assert(this->can_be_inlined_);
std::string token;
token += '{';
bool is_first = true;
for(const auto& kv : v)
{
// in inline tables, trailing comma is not allowed (toml-lang #569).
if(is_first) {is_first = false;} else {token += ',';}
token += format_key(kv.first);
token += '=';
token += visit(serializer(
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
}
token += '}';
return token;
}
std::string make_multiline_table(const table_type& v) const
{
std::string token;
// print non-table elements first.
// ```toml
// [foo] # a table we're writing now here
// key = "value" # <- non-table element, "key"
// # ...
// [foo.bar] # <- table element, "bar"
// ```
// because after printing [foo.bar], the remaining non-table values will
// be assigned into [foo.bar], not [foo]. Those values should be printed
// earlier.
for(const auto& kv : v)
{
if(kv.second.is_table() || is_array_of_tables(kv.second))
{
continue;
}
token += write_comments(kv.second);
const auto key_and_sep = format_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0;
token += key_and_sep;
token += visit(serializer(residual_width, this->float_prec_,
/*can be inlined*/ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
if(token.back() != '\n')
{
token += '\n';
}
}
// normal tables / array of tables
// after multiline table appeared, the other tables cannot be inline
// because the table would be assigned into the table.
// [foo]
// ...
// bar = {...} # <- bar will be a member of [foo].
bool multiline_table_printed = false;
for(const auto& kv : v)
{
if(!kv.second.is_table() && !is_array_of_tables(kv.second))
{
continue; // other stuff are already serialized. skip them.
}
std::vector<toml::key> ks(this->keys_);
ks.push_back(kv.first);
auto tmp = visit(serializer(this->width_, this->float_prec_,
!multiline_table_printed, this->no_comment_, ks,
/*has_comment*/ !kv.second.comments().empty()), kv.second);
// If it is the first time to print a multi-line table, it would be
// helpful to separate normal key-value pair and subtables by a
// newline.
// (this checks if the current key-value pair contains newlines.
// but it is not perfect because multi-line string can also contain
// a newline. in such a case, an empty line will be written) TODO
if((!multiline_table_printed) &&
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
{
multiline_table_printed = true;
token += '\n'; // separate key-value pairs and subtables
token += write_comments(kv.second);
token += tmp;
// care about recursive tables (all tables in each level prints
// newline and there will be a full of newlines)
if(tmp.substr(tmp.size() - 2, 2) != "\n\n" &&
tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" )
{
token += '\n';
}
}
else
{
token += write_comments(kv.second);
token += tmp;
token += '\n';
}
}
return token;
}
std::string make_array_of_tables(const array_type& v) const
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined, we can format it as the following.
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
// This function checks if inlinization is possible or not, and then
// format the array-of-tables in a proper way.
//
// Note about comments:
//
// If the array itself has a comment (value_has_comment_ == true), we
// should try to make it inline.
// ```toml
// # comment about array
// array = [
// # comment about table element
// {of = "table"}
// ]
// ```
// If it is formatted as a multiline table, the two comments becomes
// indistinguishable.
// ```toml
// # comment about array
// # comment about table element
// [[array]]
// of = "table"
// ```
// So we need to try to make it inline, and it force-inlines regardless
// of the line width limit.
// It may fail if the element of a table has comment. In that case,
// the array-of-tables will be formatted as a multiline table.
if(this->can_be_inlined_ || this->value_has_comment_)
{
std::string token;
if(!keys_.empty())
{
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
// write comments for the table itself
token += write_comments(item);
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
// if the value itself has a comment, ignore the line width limit
if( ! this->value_has_comment_)
{
failed = true;
break;
}
}
token += t;
token += ",\n";
}
if( ! failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
token += write_comments(item);
token += "[[";
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
std::string write_comments(const value_type& v) const
{
std::string retval;
if(this->no_comment_) {return retval;}
for(const auto& c : v.comments())
{
retval += '#';
retval += c;
retval += '\n';
}
return retval;
}
bool is_array_of_tables(const value_type& v) const
{
if(!v.is_array() || v.as_array().empty()) {return false;}
return is_array_of_tables(v.as_array());
}
bool is_array_of_tables(const array_type& v) const
{
// Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to
// check all the element in an array to check if the array is an array
// of tables.
return std::all_of(v.begin(), v.end(), [](const value_type& elem) {
return elem.is_table();
});
}
private:
bool can_be_inlined_;
bool no_comment_;
bool value_has_comment_;
int float_prec_;
std::size_t width_;
std::vector<toml::key> keys_;
};
template<typename C,
template<typename ...> class M, template<typename ...> class V>
std::string
format(const basic_value<C, M, V>& v, std::size_t w = 80u,
int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool no_comment = false, bool force_inline = false)
{
using value_type = basic_value<C, M, V>;
// if value is a table, it is considered to be a root object.
// the root object can't be an inline table.
if(v.is_table())
{
std::ostringstream oss;
if(!v.comments().empty())
{
oss << v.comments();
oss << '\n'; // to split the file comment from the first element
}
const auto serialized = visit(serializer<value_type>(w, fprec, false, no_comment), v);
oss << serialized;
return oss.str();
}
return visit(serializer<value_type>(w, fprec, force_inline), v);
}
namespace detail
{
template<typename charT, typename traits>
int comment_index(std::basic_ostream<charT, traits>&)
{
static const int index = std::ios_base::xalloc();
return index;
}
} // detail
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
nocomment(std::basic_ostream<charT, traits>& os)
{
// by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 1;
return os;
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
showcomment(std::basic_ostream<charT, traits>& os)
{
// by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 0;
return os;
}
template<typename charT, typename traits, typename C,
template<typename ...> class M, template<typename ...> class V>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
{
using value_type = basic_value<C, M, V>;
// get status of std::setw().
const auto w = static_cast<std::size_t>(os.width());
const int fprec = static_cast<int>(os.precision());
os.width(0);
// by default, iword is initialized by 0. And by default, toml11 outputs
// comments. So `0` means showcomment. 1 means nocommnet.
const bool no_comment = (1 == os.iword(detail::comment_index(os)));
if(!no_comment && v.is_table() && !v.comments().empty())
{
os << v.comments();
os << '\n'; // to split the file comment from the first element
}
// the root object can't be an inline table. so pass `false`.
const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v);
os << serialized;
// if v is a non-table value, and has only one comment, then
// put a comment just after a value. in the following way.
//
// ```toml
// key = "value" # comment.
// ```
//
// Since the top-level toml object is a table, one who want to put a
// non-table toml value must use this in a following way.
//
// ```cpp
// toml::value v;
// std::cout << "user-defined-key = " << v << std::endl;
// ```
//
// In this case, it is impossible to put comments before key-value pair.
// The only way to preserve comments is to put all of them after a value.
if(!no_comment && !v.is_table() && !v.comments().empty())
{
os << " #";
for(const auto& c : v.comments()) {os << c;}
}
return os;
}
} // toml
#endif// TOML11_SERIALIZER_HPP
+239
View File
@@ -0,0 +1,239 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_SOURCE_LOCATION_HPP
#define TOML11_SOURCE_LOCATION_HPP
#include <cstdint>
#include <sstream>
#include "region.hpp"
namespace toml
{
// A struct to contain location in a toml file.
// The interface imitates std::experimental::source_location,
// but not completely the same.
//
// It would be constructed by toml::value. It can be used to generate
// user-defined error messages.
//
// - std::uint_least32_t line() const noexcept
// - returns the line number where the region is on.
// - std::uint_least32_t column() const noexcept
// - returns the column number where the region starts.
// - std::uint_least32_t region() const noexcept
// - returns the size of the region.
//
// +-- line() +-- region of interest (region() == 9)
// v .---+---.
// 12 | value = "foo bar"
// ^
// +-- column()
//
// - std::string const& file_name() const noexcept;
// - name of the file.
// - std::string const& line_str() const noexcept;
// - the whole line that contains the region of interest.
//
struct source_location
{
public:
source_location()
: line_num_(1), column_num_(1), region_size_(1),
file_name_("unknown file"), line_str_("")
{}
explicit source_location(const detail::region_base* reg)
: line_num_(1), column_num_(1), region_size_(1),
file_name_("unknown file"), line_str_("")
{
if(reg)
{
if(reg->line_num() != detail::region_base().line_num())
{
line_num_ = static_cast<std::uint_least32_t>(
std::stoul(reg->line_num()));
}
column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1);
region_size_ = static_cast<std::uint_least32_t>(reg->size());
file_name_ = reg->name();
line_str_ = reg->line();
}
}
explicit source_location(const detail::region& reg)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))),
column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(reg.size())),
file_name_(reg.name()),
line_str_ (reg.line())
{}
explicit source_location(const detail::location& loc)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))),
column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(loc.size())),
file_name_(loc.name()),
line_str_ (loc.line())
{}
~source_location() = default;
source_location(source_location const&) = default;
source_location(source_location &&) = default;
source_location& operator=(source_location const&) = default;
source_location& operator=(source_location &&) = default;
std::uint_least32_t line() const noexcept {return line_num_;}
std::uint_least32_t column() const noexcept {return column_num_;}
std::uint_least32_t region() const noexcept {return region_size_;}
std::string const& file_name() const noexcept {return file_name_;}
std::string const& line_str() const noexcept {return line_str_;}
private:
std::uint_least32_t line_num_;
std::uint_least32_t column_num_;
std::uint_least32_t region_size_;
std::string file_name_;
std::string line_str_;
};
namespace detail
{
// internal error message generation.
inline std::string format_underline(const std::string& message,
const std::vector<std::pair<source_location, std::string>>& loc_com,
const std::vector<std::string>& helps = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
std::size_t line_num_width = 0;
for(const auto& lc : loc_com)
{
std::uint_least32_t line = lc.first.line();
std::size_t digit = 0;
while(line != 0)
{
line /= 10;
digit += 1;
}
line_num_width = (std::max)(line_num_width, digit);
}
// 1 is the minimum width
line_num_width = std::max<std::size_t>(line_num_width, 1);
std::ostringstream retval;
if(color::should_color() || colorize)
{
retval << color::colorize; // turn on ANSI color
}
// XXX
// Here, before `colorize` support, it does not output `[error]` prefix
// automatically. So some user may output it manually and this change may
// duplicate the prefix. To avoid it, check the first 7 characters and
// if it is "[error]", it removes that part from the message shown.
if(message.size() > 7 && message.substr(0, 7) == "[error]")
{
retval
#ifndef TOML11_NO_ERROR_PREFIX
<< color::bold << color::red << "[error]" << color::reset
#endif
<< color::bold << message.substr(7) << color::reset << '\n';
}
else
{
retval
#ifndef TOML11_NO_ERROR_PREFIX
<< color::bold << color::red << "[error] " << color::reset
#endif
<< color::bold << message << color::reset << '\n';
}
const auto format_one_location = [line_num_width]
(std::ostringstream& oss,
const source_location& loc, const std::string& comment) -> void
{
oss << ' ' << color::bold << color::blue
<< std::setw(static_cast<int>(line_num_width))
<< std::right << loc.line() << " | " << color::reset
<< loc.line_str() << '\n';
oss << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " | " << color::reset
<< make_string(loc.column()-1 /*1-origin*/, ' ');
if(loc.region() == 1)
{
// invalid
// ^------
oss << color::bold << color::red << "^---" << color::reset;
}
else
{
// invalid
// ~~~~~~~
const auto underline_len = (std::min)(
static_cast<std::size_t>(loc.region()), loc.line_str().size());
oss << color::bold << color::red
<< make_string(underline_len, '~') << color::reset;
}
oss << ' ';
oss << comment;
return;
};
assert(!loc_com.empty());
// --> example.toml
// |
retval << color::bold << color::blue << " --> " << color::reset
<< loc_com.front().first.file_name() << '\n';
retval << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
// 1 | key value
// | ^--- missing =
format_one_location(retval, loc_com.front().first, loc_com.front().second);
// process the rest of the locations
for(std::size_t i=1; i<loc_com.size(); ++i)
{
const auto& prev = loc_com.at(i-1);
const auto& curr = loc_com.at(i);
retval << '\n';
// if the filenames are the same, print "..."
if(prev.first.file_name() == curr.first.file_name())
{
retval << color::bold << color::blue << " ...\n" << color::reset;
}
else // if filename differs, print " --> filename.toml" again
{
retval << color::bold << color::blue << " --> " << color::reset
<< curr.first.file_name() << '\n';
retval << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
}
format_one_location(retval, curr.first, curr.second);
}
if(!helps.empty())
{
retval << '\n';
retval << make_string(line_num_width + 1, ' ');
retval << color::bold << color::blue << " |" << color::reset;
for(const auto& help : helps)
{
retval << color::bold << "\nHint: " << color::reset;
retval << help;
}
}
return retval.str();
}
} // detail
} // toml
#endif// TOML11_SOURCE_LOCATION_HPP
+43
View File
@@ -0,0 +1,43 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_STORAGE_HPP
#define TOML11_STORAGE_HPP
#include "utility.hpp"
namespace toml
{
namespace detail
{
// this contains pointer and deep-copy the content if copied.
// to avoid recursive pointer.
template<typename T>
struct storage
{
using value_type = T;
explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
explicit storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
~storage() = default;
storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {}
storage& operator=(const storage& rhs)
{
this->ptr = toml::make_unique<T>(*rhs.ptr);
return *this;
}
storage(storage&&) = default;
storage& operator=(storage&&) = default;
bool is_ok() const noexcept {return static_cast<bool>(ptr);}
value_type& value() & noexcept {return *ptr;}
value_type const& value() const& noexcept {return *ptr;}
value_type&& value() && noexcept {return std::move(*ptr);}
private:
std::unique_ptr<value_type> ptr;
};
} // detail
} // toml
#endif// TOML11_STORAGE_HPP
+228
View File
@@ -0,0 +1,228 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP
#include "version.hpp"
#include <cstdint>
#include <algorithm>
#include <string>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
#define TOML11_USING_STRING_VIEW 1
#include <string_view>
#endif
#endif
namespace toml
{
enum class string_t : std::uint8_t
{
basic = 0,
literal = 1,
};
struct string
{
string() = default;
~string() = default;
string(const string& s) = default;
string(string&& s) = default;
string& operator=(const string& s) = default;
string& operator=(string&& s) = default;
string(const std::string& s): kind(string_t::basic), str(s){}
string(const std::string& s, string_t k): kind(k), str(s){}
string(const char* s): kind(string_t::basic), str(s){}
string(const char* s, string_t k): kind(k), str(s){}
string(std::string&& s): kind(string_t::basic), str(std::move(s)){}
string(std::string&& s, string_t k): kind(k), str(std::move(s)){}
string& operator=(const std::string& s)
{kind = string_t::basic; str = s; return *this;}
string& operator=(std::string&& s)
{kind = string_t::basic; str = std::move(s); return *this;}
operator std::string& () & noexcept {return str;}
operator std::string const& () const& noexcept {return str;}
operator std::string&& () && noexcept {return std::move(str);}
string& operator+=(const char* rhs) {str += rhs; return *this;}
string& operator+=(const char rhs) {str += rhs; return *this;}
string& operator+=(const std::string& rhs) {str += rhs; return *this;}
string& operator+=(const string& rhs) {str += rhs.str; return *this;}
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
explicit string(std::string_view s): kind(string_t::basic), str(s){}
string(std::string_view s, string_t k): kind(k), str(s){}
string& operator=(std::string_view s)
{kind = string_t::basic; str = s; return *this;}
explicit operator std::string_view() const noexcept
{return std::string_view(str);}
string& operator+=(const std::string_view& rhs) {str += rhs; return *this;}
#endif
string_t kind;
std::string str;
};
inline bool operator==(const string& lhs, const string& rhs)
{
return lhs.kind == rhs.kind && lhs.str == rhs.str;
}
inline bool operator!=(const string& lhs, const string& rhs)
{
return !(lhs == rhs);
}
inline bool operator<(const string& lhs, const string& rhs)
{
return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind);
}
inline bool operator>(const string& lhs, const string& rhs)
{
return rhs < lhs;
}
inline bool operator<=(const string& lhs, const string& rhs)
{
return !(rhs < lhs);
}
inline bool operator>=(const string& lhs, const string& rhs)
{
return !(lhs < rhs);
}
inline bool
operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;}
inline bool
operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;}
inline bool
operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;}
inline bool
operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;}
inline bool
operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;}
inline bool
operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;}
inline bool
operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;}
inline bool
operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;}
inline bool
operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;}
inline bool
operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;}
inline bool
operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;}
inline bool
operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;}
inline bool
operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);}
inline bool
operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);}
inline bool
operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);}
inline bool
operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);}
inline bool
operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);}
inline bool
operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);}
inline bool
operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;}
inline bool
operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;}
inline bool
operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;}
inline bool
operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;}
inline bool
operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;}
inline bool
operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const string& s)
{
if(s.kind == string_t::basic)
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
{
// it contains newline. make it multiline string.
os << "\"\"\"\n";
for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i)
{
switch(*i)
{
case '\\': {os << "\\\\"; break;}
case '\"': {os << "\\\""; break;}
case '\b': {os << "\\b"; break;}
case '\t': {os << "\\t"; break;}
case '\f': {os << "\\f"; break;}
case '\n': {os << '\n'; break;}
case '\r':
{
// since it is a multiline string,
// CRLF is not needed to be escaped.
if(std::next(i) != e && *std::next(i) == '\n')
{
os << "\r\n";
++i;
}
else
{
os << "\\r";
}
break;
}
default: {os << *i; break;}
}
}
os << "\\\n\"\"\"";
return os;
}
// no newline. make it inline.
os << "\"";
for(const auto c : s.str)
{
switch(c)
{
case '\\': {os << "\\\\"; break;}
case '\"': {os << "\\\""; break;}
case '\b': {os << "\\b"; break;}
case '\t': {os << "\\t"; break;}
case '\f': {os << "\\f"; break;}
case '\n': {os << "\\n"; break;}
case '\r': {os << "\\r"; break;}
default : {os << c; break;}
}
}
os << "\"";
return os;
}
// the string `s` is literal-string.
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{
// contains newline or single quote. make it multiline.
os << "'''\n" << s.str << "'''";
return os;
}
// normal literal string
os << '\'' << s.str << '\'';
return os;
}
} // toml
#endif// TOML11_STRING_H
+328
View File
@@ -0,0 +1,328 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS_HPP
#include "from.hpp"
#include "into.hpp"
#include "version.hpp"
#include <chrono>
#include <forward_list>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
#include <string_view>
#endif // has_include(<string_view>)
#endif // cplusplus >= C++17
namespace toml
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
class basic_value;
namespace detail
{
// ---------------------------------------------------------------------------
// check whether type T is a kind of container/map class
struct has_iterator_impl
{
template<typename T> static std::true_type check(typename T::iterator*);
template<typename T> static std::false_type check(...);
};
struct has_value_type_impl
{
template<typename T> static std::true_type check(typename T::value_type*);
template<typename T> static std::false_type check(...);
};
struct has_key_type_impl
{
template<typename T> static std::true_type check(typename T::key_type*);
template<typename T> static std::false_type check(...);
};
struct has_mapped_type_impl
{
template<typename T> static std::true_type check(typename T::mapped_type*);
template<typename T> static std::false_type check(...);
};
struct has_reserve_method_impl
{
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*);
};
struct has_push_back_method_impl
{
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*);
};
struct is_comparable_impl
{
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>() < std::declval<T>())*);
};
struct has_from_toml_method_impl
{
template<typename T, typename C,
template<typename ...> class Tb, template<typename ...> class A>
static std::true_type check(
decltype(std::declval<T>().from_toml(
std::declval<::toml::basic_value<C, Tb, A>>()))*);
template<typename T, typename C,
template<typename ...> class Tb, template<typename ...> class A>
static std::false_type check(...);
};
struct has_into_toml_method_impl
{
template<typename T>
static std::true_type check(decltype(std::declval<T>().into_toml())*);
template<typename T>
static std::false_type check(...);
};
struct has_specialized_from_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::from<T>)>
static std::true_type check(::toml::from<T>*);
};
struct has_specialized_into_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
static std::true_type check(::toml::from<T>*);
};
/// Intel C++ compiler can not use decltype in parent class declaration, here
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
#ifdef __INTEL_COMPILER
#define decltype(...) std::enable_if<true, decltype(__VA_ARGS__)>::type
#endif
template<typename T>
struct has_iterator : decltype(has_iterator_impl::check<T>(nullptr)){};
template<typename T>
struct has_value_type : decltype(has_value_type_impl::check<T>(nullptr)){};
template<typename T>
struct has_key_type : decltype(has_key_type_impl::check<T>(nullptr)){};
template<typename T>
struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){};
template<typename T>
struct has_reserve_method : decltype(has_reserve_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_push_back_method : decltype(has_push_back_method_impl::check<T>(nullptr)){};
template<typename T>
struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){};
template<typename T, typename C,
template<typename ...> class Tb, template<typename ...> class A>
struct has_from_toml_method
: decltype(has_from_toml_method_impl::check<T, C, Tb, A>(nullptr)){};
template<typename T>
struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_from : decltype(has_specialized_from_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_into : decltype(has_specialized_into_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER
#undef decltype
#endif
// ---------------------------------------------------------------------------
// C++17 and/or/not
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template<typename ...> struct conjunction : std::true_type{};
template<typename T> struct conjunction<T> : T{};
template<typename T, typename ... Ts>
struct conjunction<T, Ts...> :
std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type
{};
template<typename ...> struct disjunction : std::false_type{};
template<typename T> struct disjunction<T> : T {};
template<typename T, typename ... Ts>
struct disjunction<T, Ts...> :
std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type
{};
template<typename T>
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
#endif
// ---------------------------------------------------------------------------
// type checkers
template<typename T> struct is_std_pair : std::false_type{};
template<typename T1, typename T2>
struct is_std_pair<std::pair<T1, T2>> : std::true_type{};
template<typename T> struct is_std_tuple : std::false_type{};
template<typename ... Ts>
struct is_std_tuple<std::tuple<Ts...>> : std::true_type{};
template<typename T> struct is_std_forward_list : std::false_type{};
template<typename T>
struct is_std_forward_list<std::forward_list<T>> : std::true_type{};
template<typename T> struct is_chrono_duration: std::false_type{};
template<typename Rep, typename Period>
struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{};
template<typename T>
struct is_map : conjunction< // map satisfies all the following conditions
has_iterator<T>, // has T::iterator
has_value_type<T>, // has T::value_type
has_key_type<T>, // has T::key_type
has_mapped_type<T> // has T::mapped_type
>{};
template<typename T> struct is_map<T&> : is_map<T>{};
template<typename T> struct is_map<T const&> : is_map<T>{};
template<typename T> struct is_map<T volatile&> : is_map<T>{};
template<typename T> struct is_map<T const volatile&> : is_map<T>{};
template<typename T>
struct is_container : conjunction<
negation<is_map<T>>, // not a map
negation<std::is_same<T, std::string>>, // not a std::string
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
negation<std::is_same<T, std::string_view>>, // not a std::string_view
#endif // has_include(<string_view>)
#endif
has_iterator<T>, // has T::iterator
has_value_type<T> // has T::value_type
>{};
template<typename T> struct is_container<T&> : is_container<T>{};
template<typename T> struct is_container<T const&> : is_container<T>{};
template<typename T> struct is_container<T volatile&> : is_container<T>{};
template<typename T> struct is_container<T const volatile&> : is_container<T>{};
template<typename T>
struct is_basic_value: std::false_type{};
template<typename T> struct is_basic_value<T&> : is_basic_value<T>{};
template<typename T> struct is_basic_value<T const&> : is_basic_value<T>{};
template<typename T> struct is_basic_value<T volatile&> : is_basic_value<T>{};
template<typename T> struct is_basic_value<T const volatile&> : is_basic_value<T>{};
template<typename C, template<typename ...> class M, template<typename ...> class V>
struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
// ---------------------------------------------------------------------------
// C++14 index_sequence
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::index_sequence;
using std::make_index_sequence;
#else
template<std::size_t ... Ns> struct index_sequence{};
template<typename IS, std::size_t N> struct push_back_index_sequence{};
template<std::size_t N, std::size_t ... Ns>
struct push_back_index_sequence<index_sequence<Ns...>, N>
{
typedef index_sequence<Ns..., N> type;
};
template<std::size_t N>
struct index_sequence_maker
{
typedef typename push_back_index_sequence<
typename index_sequence_maker<N-1>::type, N>::type type;
};
template<>
struct index_sequence_maker<0>
{
typedef index_sequence<0> type;
};
template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type;
#endif // cplusplus >= 2014
// ---------------------------------------------------------------------------
// C++14 enable_if_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::enable_if_t;
#else
template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;
#endif // cplusplus >= 2014
// ---------------------------------------------------------------------------
// return_type_of_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;
#else
// result_of is deprecated after C++17
template<typename F, typename ... Args>
using return_type_of_t = typename std::result_of<F(Args...)>::type;
#endif
// ---------------------------------------------------------------------------
// is_string_literal
//
// to use this, pass `typename remove_reference<T>::type` to T.
template<typename T>
struct is_string_literal:
disjunction<
std::is_same<const char*, T>,
conjunction<
std::is_array<T>,
std::is_same<const char, typename std::remove_extent<T>::type>
>
>{};
// ---------------------------------------------------------------------------
// C++20 remove_cvref_t
template<typename T>
struct remove_cvref
{
using type = typename std::remove_cv<
typename std::remove_reference<T>::type>::type;
};
template<typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
}// detail
}//toml
#endif // TOML_TRAITS
+173
View File
@@ -0,0 +1,173 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_TYPES_HPP
#define TOML11_TYPES_HPP
#include <unordered_map>
#include <vector>
#include "comments.hpp"
#include "datetime.hpp"
#include "string.hpp"
#include "traits.hpp"
namespace toml
{
template<typename Comment, // discard/preserve_comment
template<typename ...> class Table, // map-like class
template<typename ...> class Array> // vector-like class
class basic_value;
using character = char;
using key = std::string;
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wshadow"
#endif
using boolean = bool;
using integer = std::int64_t;
using floating = double; // "float" is a keyword, cannot use it here.
// the following stuffs are structs defined here, so aliases are not needed.
// - string
// - offset_datetime
// - offset_datetime
// - local_datetime
// - local_date
// - local_time
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
// default toml::value and default array/table. these are defined after defining
// basic_value itself.
// using value = basic_value<discard_comments, std::unordered_map, std::vector>;
// using array = typename value::array_type;
// using table = typename value::table_type;
// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in
// GCC -Wshadow=global.
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# if 7 <= __GNUC__
# pragma GCC diagnostic ignored "-Wshadow=global"
# else // gcc-6 or older
# pragma GCC diagnostic ignored "-Wshadow"
# endif
#endif
enum class value_t : std::uint8_t
{
empty = 0,
boolean = 1,
integer = 2,
floating = 3,
string = 4,
offset_datetime = 5,
local_datetime = 6,
local_date = 7,
local_time = 8,
array = 9,
table = 10,
};
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
template<typename charT, typename traits>
inline std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, value_t t)
{
switch(t)
{
case value_t::boolean : os << "boolean"; return os;
case value_t::integer : os << "integer"; return os;
case value_t::floating : os << "floating"; return os;
case value_t::string : os << "string"; return os;
case value_t::offset_datetime : os << "offset_datetime"; return os;
case value_t::local_datetime : os << "local_datetime"; return os;
case value_t::local_date : os << "local_date"; return os;
case value_t::local_time : os << "local_time"; return os;
case value_t::array : os << "array"; return os;
case value_t::table : os << "table"; return os;
case value_t::empty : os << "empty"; return os;
default : os << "unknown"; return os;
}
}
template<typename charT = char,
typename traits = std::char_traits<charT>,
typename alloc = std::allocator<charT>>
inline std::basic_string<charT, traits, alloc> stringize(value_t t)
{
std::basic_ostringstream<charT, traits, alloc> oss;
oss << t;
return oss.str();
}
namespace detail
{
// helper to define a type that represents a value_t value.
template<value_t V>
using value_t_constant = std::integral_constant<value_t, V>;
// meta-function that convertes from value_t to the exact toml type that corresponds to.
// It takes toml::basic_value type because array and table types depend on it.
template<value_t t, typename Value> struct enum_to_type {using type = void ;};
template<typename Value> struct enum_to_type<value_t::empty , Value>{using type = void ;};
template<typename Value> struct enum_to_type<value_t::boolean , Value>{using type = boolean ;};
template<typename Value> struct enum_to_type<value_t::integer , Value>{using type = integer ;};
template<typename Value> struct enum_to_type<value_t::floating , Value>{using type = floating ;};
template<typename Value> struct enum_to_type<value_t::string , Value>{using type = string ;};
template<typename Value> struct enum_to_type<value_t::offset_datetime, Value>{using type = offset_datetime ;};
template<typename Value> struct enum_to_type<value_t::local_datetime , Value>{using type = local_datetime ;};
template<typename Value> struct enum_to_type<value_t::local_date , Value>{using type = local_date ;};
template<typename Value> struct enum_to_type<value_t::local_time , Value>{using type = local_time ;};
template<typename Value> struct enum_to_type<value_t::array , Value>{using type = typename Value::array_type;};
template<typename Value> struct enum_to_type<value_t::table , Value>{using type = typename Value::table_type;};
// meta-function that converts from an exact toml type to the enum that corresponds to.
template<typename T, typename Value>
struct type_to_enum : std::conditional<
std::is_same<T, typename Value::array_type>::value, // if T == array_type,
value_t_constant<value_t::array>, // then value_t::array
typename std::conditional< // else...
std::is_same<T, typename Value::table_type>::value, // if T == table_type
value_t_constant<value_t::table>, // then value_t::table
value_t_constant<value_t::empty> // else value_t::empty
>::type
>::type {};
template<typename Value> struct type_to_enum<boolean , Value>: value_t_constant<value_t::boolean > {};
template<typename Value> struct type_to_enum<integer , Value>: value_t_constant<value_t::integer > {};
template<typename Value> struct type_to_enum<floating , Value>: value_t_constant<value_t::floating > {};
template<typename Value> struct type_to_enum<string , Value>: value_t_constant<value_t::string > {};
template<typename Value> struct type_to_enum<offset_datetime, Value>: value_t_constant<value_t::offset_datetime> {};
template<typename Value> struct type_to_enum<local_datetime , Value>: value_t_constant<value_t::local_datetime > {};
template<typename Value> struct type_to_enum<local_date , Value>: value_t_constant<value_t::local_date > {};
template<typename Value> struct type_to_enum<local_time , Value>: value_t_constant<value_t::local_time > {};
// meta-function that checks the type T is the same as one of the toml::* types.
template<typename T, typename Value>
struct is_exact_toml_type : disjunction<
std::is_same<T, boolean >,
std::is_same<T, integer >,
std::is_same<T, floating >,
std::is_same<T, string >,
std::is_same<T, offset_datetime>,
std::is_same<T, local_datetime >,
std::is_same<T, local_date >,
std::is_same<T, local_time >,
std::is_same<T, typename Value::array_type>,
std::is_same<T, typename Value::table_type>
>{};
template<typename T, typename V> struct is_exact_toml_type<T&, V> : is_exact_toml_type<T, V>{};
template<typename T, typename V> struct is_exact_toml_type<T const&, V> : is_exact_toml_type<T, V>{};
template<typename T, typename V> struct is_exact_toml_type<T volatile&, V> : is_exact_toml_type<T, V>{};
template<typename T, typename V> struct is_exact_toml_type<T const volatile&, V>: is_exact_toml_type<T, V>{};
} // detail
} // toml
#endif// TOML11_TYPES_H
+150
View File
@@ -0,0 +1,150 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_UTILITY_HPP
#define TOML11_UTILITY_HPP
#include <memory>
#include <sstream>
#include <utility>
#include "traits.hpp"
#include "version.hpp"
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__)
# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
#elif defined(_MSC_VER)
# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg))
#else
# define TOML11_MARK_AS_DEPRECATED
#endif
namespace toml
{
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::make_unique;
#else
template<typename T, typename ... Ts>
inline std::unique_ptr<T> make_unique(Ts&& ... args)
{
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
}
#endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014
namespace detail
{
template<typename Container>
void try_reserve_impl(Container& container, std::size_t N, std::true_type)
{
container.reserve(N);
return;
}
template<typename Container>
void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept
{
return;
}
} // detail
template<typename Container>
void try_reserve(Container& container, std::size_t N)
{
if(N <= container.size()) {return;}
detail::try_reserve_impl(container, N, detail::has_reserve_method<Container>{});
return;
}
namespace detail
{
inline std::string concat_to_string_impl(std::ostringstream& oss)
{
return oss.str();
}
template<typename T, typename ... Ts>
std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail)
{
oss << std::forward<T>(head);
return concat_to_string_impl(oss, std::forward<Ts>(tail) ... );
}
} // detail
template<typename ... Ts>
std::string concat_to_string(Ts&& ... args)
{
std::ostringstream oss;
oss << std::boolalpha << std::fixed;
return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...);
}
template<typename T>
T from_string(const std::string& str, T opt)
{
T v(opt);
std::istringstream iss(str);
iss >> v;
return v;
}
namespace detail
{
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
template<typename T>
decltype(auto) last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept
{
return last_one(std::forward<Ts>(tail)...);
}
#else // C++11
// The following code
// ```cpp
// 1 | template<typename T, typename ... Ts>
// 2 | auto last_one(T&& /*head*/, Ts&& ... tail)
// 3 | -> decltype(last_one(std::forward<Ts>(tail)...))
// 4 | {
// 5 | return last_one(std::forward<Ts>(tail)...);
// 6 | }
// ```
// does not work because the function `last_one(...)` is not yet defined at
// line #3, so `decltype()` cannot deduce the type returned from `last_one`.
// So we need to determine return type in a different way, like a meta func.
template<typename T, typename ... Ts>
struct last_one_in_pack
{
using type = typename last_one_in_pack<Ts...>::type;
};
template<typename T>
struct last_one_in_pack<T>
{
using type = T;
};
template<typename ... Ts>
using last_one_in_pack_t = typename last_one_in_pack<Ts...>::type;
template<typename T>
T&& last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t<Ts&& ...>>
last_one(T&& /*head*/, Ts&& ... tail)
{
return last_one(std::forward<Ts>(tail)...);
}
#endif
} // detail
}// toml
#endif // TOML11_UTILITY
+2037
View File
File diff suppressed because it is too large Load Diff
+42
View File
@@ -0,0 +1,42 @@
#ifndef TOML11_VERSION_HPP
#define TOML11_VERSION_HPP
// This file checks C++ version.
#ifndef __cplusplus
# error "__cplusplus is not defined"
#endif
// Since MSVC does not define `__cplusplus` correctly unless you pass
// `/Zc:__cplusplus` when compiling, the workaround macros are added.
// Those enables you to define version manually or to use MSVC specific
// version macro automatically.
//
// The value of `__cplusplus` macro is defined in the C++ standard spec, but
// MSVC ignores the value, maybe because of backward compatibility. Instead,
// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in
// the C++ standard. First we check the manual version definition, and then
// we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`.
//
// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170
//
#if defined(TOML11_ENFORCE_CXX11)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L
#elif defined(TOML11_ENFORCE_CXX14)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L
#elif defined(TOML11_ENFORCE_CXX17)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L
#elif defined(TOML11_ENFORCE_CXX20)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L
#elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER
# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG
#else
# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus
#endif
#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900
# error "toml11 requires C++11 or later."
#endif
#endif// TOML11_VERSION_HPP
File diff suppressed because it is too large Load Diff
+982
View File
@@ -0,0 +1,982 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ORDERED_MAP_H
#define TSL_ORDERED_MAP_H
#include <cstddef>
#include <cstdint>
#include <deque>
#include <functional>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "ordered_hash.h"
namespace tsl {
/**
* Implementation of an hash map using open addressing with robin hood with
* backshift delete to resolve collisions.
*
* The particularity of this hash map is that it remembers the order in which
* the elements were added and provide a way to access the structure which
* stores these values through the 'values_container()' method. The used
* container is defined by ValueTypeContainer, by default a std::deque is used
* (grows faster) but a std::vector may be used. In this case the map provides a
* 'data()' method which give a direct access to the memory used to store the
* values (which can be useful to communicate with C API's).
*
* The Key and T must be copy constructible and/or move constructible. To use
* `unordered_erase` they both must be swappable.
*
* The behaviour of the hash map is undefined if the destructor of Key or T
* throws an exception.
*
* By default the maximum size of a map is limited to 2^32 - 1 values, if needed
* this can be changed through the IndexType template parameter. Using an
* `uint64_t` will raise this limit to 2^64 - 1 values but each bucket will use
* 16 bytes instead of 8 bytes in addition to the space needed to store the
* values.
*
* Iterators invalidation:
* - clear, operator=, reserve, rehash: always invalidate the iterators (also
* invalidate end()).
* - insert, emplace, emplace_hint, operator[]: when a std::vector is used as
* ValueTypeContainer and if size() < capacity(), only end(). Otherwise all the
* iterators are invalidated if an insert occurs.
* - erase, unordered_erase: when a std::vector is used as ValueTypeContainer
* invalidate the iterator of the erased element and all the ones after the
* erased element (including end()). Otherwise all the iterators are invalidated
* if an erase occurs.
*/
template <class Key, class T, class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key, T>>,
class ValueTypeContainer = std::deque<std::pair<Key, T>, Allocator>,
class IndexType = std::uint_least32_t>
class ordered_map {
private:
template <typename U>
using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent<U>;
class KeySelect {
public:
using key_type = Key;
const key_type& operator()(
const std::pair<Key, T>& key_value) const noexcept {
return key_value.first;
}
key_type& operator()(std::pair<Key, T>& key_value) noexcept {
return key_value.first;
}
};
class ValueSelect {
public:
using value_type = T;
const value_type& operator()(
const std::pair<Key, T>& key_value) const noexcept {
return key_value.second;
}
value_type& operator()(std::pair<Key, T>& key_value) noexcept {
return key_value.second;
}
};
using ht =
detail_ordered_hash::ordered_hash<std::pair<Key, T>, KeySelect,
ValueSelect, Hash, KeyEqual, Allocator,
ValueTypeContainer, IndexType>;
public:
using key_type = typename ht::key_type;
using mapped_type = T;
using value_type = typename ht::value_type;
using size_type = typename ht::size_type;
using difference_type = typename ht::difference_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using allocator_type = typename ht::allocator_type;
using reference = typename ht::reference;
using const_reference = typename ht::const_reference;
using pointer = typename ht::pointer;
using const_pointer = typename ht::const_pointer;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
using reverse_iterator = typename ht::reverse_iterator;
using const_reverse_iterator = typename ht::const_reverse_iterator;
using values_container_type = typename ht::values_container_type;
/*
* Constructors
*/
ordered_map() : ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {}
explicit ordered_map(size_type bucket_count, const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR) {}
ordered_map(size_type bucket_count, const Allocator& alloc)
: ordered_map(bucket_count, Hash(), KeyEqual(), alloc) {}
ordered_map(size_type bucket_count, const Hash& hash, const Allocator& alloc)
: ordered_map(bucket_count, hash, KeyEqual(), alloc) {}
explicit ordered_map(const Allocator& alloc)
: ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {}
template <class InputIt>
ordered_map(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: ordered_map(bucket_count, hash, equal, alloc) {
insert(first, last);
}
template <class InputIt>
ordered_map(InputIt first, InputIt last, size_type bucket_count,
const Allocator& alloc)
: ordered_map(first, last, bucket_count, Hash(), KeyEqual(), alloc) {}
template <class InputIt>
ordered_map(InputIt first, InputIt last, size_type bucket_count,
const Hash& hash, const Allocator& alloc)
: ordered_map(first, last, bucket_count, hash, KeyEqual(), alloc) {}
ordered_map(std::initializer_list<value_type> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: ordered_map(init.begin(), init.end(), bucket_count, hash, equal,
alloc) {}
ordered_map(std::initializer_list<value_type> init, size_type bucket_count,
const Allocator& alloc)
: ordered_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(),
alloc) {}
ordered_map(std::initializer_list<value_type> init, size_type bucket_count,
const Hash& hash, const Allocator& alloc)
: ordered_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(),
alloc) {}
ordered_map& operator=(std::initializer_list<value_type> ilist) {
m_ht.clear();
m_ht.reserve(ilist.size());
m_ht.insert(ilist.begin(), ilist.end());
return *this;
}
allocator_type get_allocator() const { return m_ht.get_allocator(); }
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
reverse_iterator rbegin() noexcept { return m_ht.rbegin(); }
const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); }
const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); }
reverse_iterator rend() noexcept { return m_ht.rend(); }
const_reverse_iterator rend() const noexcept { return m_ht.rend(); }
const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert(const value_type& value) {
return m_ht.insert(value);
}
template <class P, typename std::enable_if<std::is_constructible<
value_type, P&&>::value>::type* = nullptr>
std::pair<iterator, bool> insert(P&& value) {
return m_ht.emplace(std::forward<P>(value));
}
std::pair<iterator, bool> insert(value_type&& value) {
return m_ht.insert(std::move(value));
}
iterator insert(const_iterator hint, const value_type& value) {
return m_ht.insert_hint(hint, value);
}
template <class P, typename std::enable_if<std::is_constructible<
value_type, P&&>::value>::type* = nullptr>
iterator insert(const_iterator hint, P&& value) {
return m_ht.emplace_hint(hint, std::forward<P>(value));
}
iterator insert(const_iterator hint, value_type&& value) {
return m_ht.insert_hint(hint, std::move(value));
}
template <class InputIt>
void insert(InputIt first, InputIt last) {
m_ht.insert(first, last);
}
void insert(std::initializer_list<value_type> ilist) {
m_ht.insert(ilist.begin(), ilist.end());
}
template <class M>
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
return m_ht.insert_or_assign(k, std::forward<M>(obj));
}
template <class M>
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
}
template <class M>
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
}
template <class M>
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
}
/**
* Due to the way elements are stored, emplace will need to move or copy the
* key-value once. The method is equivalent to
* insert(value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
return m_ht.emplace(std::forward<Args>(args)...);
}
/**
* Due to the way elements are stored, emplace_hint will need to move or copy
* the key-value once. The method is equivalent to insert(hint,
* value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template <class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) {
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
}
template <class... Args>
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
return m_ht.try_emplace(k, std::forward<Args>(args)...);
}
template <class... Args>
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
}
template <class... Args>
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
return m_ht.try_emplace_hint(hint, k, std::forward<Args>(args)...);
}
template <class... Args>
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
return m_ht.try_emplace_hint(hint, std::move(k),
std::forward<Args>(args)...);
}
/**
* When erasing an element, the insert order will be preserved and no holes
* will be present in the container returned by 'values_container()'.
*
* The method is in O(bucket_count()), if the order is not important
* 'unordered_erase(...)' method is faster with an O(1) average complexity.
*/
iterator erase(iterator pos) { return m_ht.erase(pos); }
/**
* @copydoc erase(iterator pos)
*/
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
/**
* @copydoc erase(iterator pos)
*/
iterator erase(const_iterator first, const_iterator last) {
return m_ht.erase(first, last);
}
/**
* @copydoc erase(iterator pos)
*/
size_type erase(const key_type& key) { return m_ht.erase(key); }
/**
* @copydoc erase(iterator pos)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup to the value if you already have the hash.
*/
size_type erase(const key_type& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
/**
* @copydoc erase(iterator pos)
*
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key) {
return m_ht.erase(key);
}
/**
* @copydoc erase(const key_type& key, std::size_t precalculated_hash)
*
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
void swap(ordered_map& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
T& at(const Key& key) { return m_ht.at(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
T& at(const Key& key, std::size_t precalculated_hash) {
return m_ht.at(key, precalculated_hash);
}
const T& at(const Key& key) const { return m_ht.at(key); }
/**
* @copydoc at(const Key& key, std::size_t precalculated_hash)
*/
const T& at(const Key& key, std::size_t precalculated_hash) const {
return m_ht.at(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
T& at(const K& key) {
return m_ht.at(key);
}
/**
* @copydoc at(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
T& at(const K& key, std::size_t precalculated_hash) {
return m_ht.at(key, precalculated_hash);
}
/**
* @copydoc at(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const T& at(const K& key) const {
return m_ht.at(key);
}
/**
* @copydoc at(const K& key, std::size_t precalculated_hash)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const T& at(const K& key, std::size_t precalculated_hash) const {
return m_ht.at(key, precalculated_hash);
}
T& operator[](const Key& key) { return m_ht[key]; }
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
size_type count(const Key& key) const { return m_ht.count(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
size_type count(const Key& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key) const {
return m_ht.count(key);
}
/**
* @copydoc count(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
iterator find(const Key& key) { return m_ht.find(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
iterator find(const Key& key, std::size_t precalculated_hash) {
return m_ht.find(key, precalculated_hash);
}
const_iterator find(const Key& key) const { return m_ht.find(key); }
/**
* @copydoc find(const Key& key, std::size_t precalculated_hash)
*/
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key) {
return m_ht.find(key);
}
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key, std::size_t precalculated_hash) {
return m_ht.find(key, precalculated_hash);
}
/**
* @copydoc find(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key) const {
return m_ht.find(key);
}
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
bool contains(const Key& key) const { return m_ht.contains(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
bool contains(const Key& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key) const {
return m_ht.contains(key);
}
/**
* @copydoc contains(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
std::pair<iterator, iterator> equal_range(const Key& key) {
return m_ht.equal_range(key);
}
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
std::pair<iterator, iterator> equal_range(const Key& key,
std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(
const Key& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key) {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key,
std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* @copydoc equal_range(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key) const {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(
const K& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count) { m_ht.rehash(count); }
void reserve(size_type count) { m_ht.reserve(count); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Convert a const_iterator to an iterator.
*/
iterator mutable_iterator(const_iterator pos) {
return m_ht.mutable_iterator(pos);
}
/**
* Requires index <= size().
*
* Return an iterator to the element at index. Return end() if index ==
* size().
*/
iterator nth(size_type index) { return m_ht.nth(index); }
/**
* @copydoc nth(size_type index)
*/
const_iterator nth(size_type index) const { return m_ht.nth(index); }
/**
* Return const_reference to the first element. Requires the container to not
* be empty.
*/
const_reference front() const { return m_ht.front(); }
/**
* Return const_reference to the last element. Requires the container to not
* be empty.
*/
const_reference back() const { return m_ht.back(); }
/**
* Only available if ValueTypeContainer is a std::vector. Same as calling
* 'values_container().data()'.
*/
template <class U = values_container_type,
typename std::enable_if<
tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
const typename values_container_type::value_type* data() const noexcept {
return m_ht.data();
}
/**
* Return the container in which the values are stored. The values are in the
* same order as the insertion order and are contiguous in the structure, no
* holes (size() == values_container().size()).
*/
const values_container_type& values_container() const noexcept {
return m_ht.values_container();
}
/**
* Release the container in which the values are stored.
*
* The map is empty after this operation.
*/
values_container_type release() { return m_ht.release(); }
template <class U = values_container_type,
typename std::enable_if<
tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
size_type capacity() const noexcept {
return m_ht.capacity();
}
void shrink_to_fit() { m_ht.shrink_to_fit(); }
/**
* Insert the value before pos shifting all the elements on the right of pos
* (including pos) one position to the right.
*
* O(bucket_count()) runtime complexity.
*/
std::pair<iterator, bool> insert_at_position(const_iterator pos,
const value_type& value) {
return m_ht.insert_at_position(pos, value);
}
/**
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
*/
std::pair<iterator, bool> insert_at_position(const_iterator pos,
value_type&& value) {
return m_ht.insert_at_position(pos, std::move(value));
}
/**
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
*
* Same as insert_at_position(pos, value_type(std::forward<Args>(args)...),
* mainly here for coherence.
*/
template <class... Args>
std::pair<iterator, bool> emplace_at_position(const_iterator pos,
Args&&... args) {
return m_ht.emplace_at_position(pos, std::forward<Args>(args)...);
}
/**
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
*/
template <class... Args>
std::pair<iterator, bool> try_emplace_at_position(const_iterator pos,
const key_type& k,
Args&&... args) {
return m_ht.try_emplace_at_position(pos, k, std::forward<Args>(args)...);
}
/**
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
*/
template <class... Args>
std::pair<iterator, bool> try_emplace_at_position(const_iterator pos,
key_type&& k,
Args&&... args) {
return m_ht.try_emplace_at_position(pos, std::move(k),
std::forward<Args>(args)...);
}
void pop_back() { m_ht.pop_back(); }
/**
* Faster erase operation with an O(1) average complexity but it doesn't
* preserve the insertion order.
*
* If an erasure occurs, the last element of the map will take the place of
* the erased element.
*/
iterator unordered_erase(iterator pos) { return m_ht.unordered_erase(pos); }
/**
* @copydoc unordered_erase(iterator pos)
*/
iterator unordered_erase(const_iterator pos) {
return m_ht.unordered_erase(pos);
}
/**
* @copydoc unordered_erase(iterator pos)
*/
size_type unordered_erase(const key_type& key) {
return m_ht.unordered_erase(key);
}
/**
* @copydoc unordered_erase(iterator pos)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
size_type unordered_erase(const key_type& key,
std::size_t precalculated_hash) {
return m_ht.unordered_erase(key, precalculated_hash);
}
/**
* @copydoc unordered_erase(iterator pos)
*
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type unordered_erase(const K& key) {
return m_ht.unordered_erase(key);
}
/**
* @copydoc unordered_erase(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type unordered_erase(const K& key, std::size_t precalculated_hash) {
return m_ht.unordered_erase(key, precalculated_hash);
}
/**
* Serialize the map through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the
* following call:
* - `template<typename U> void operator()(const U& value);` where the types
* `std::uint64_t`, `float` and `std::pair<Key, T>` must be supported for U.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for
* floats, ...) of the types it serializes in the hands of the `Serializer`
* function object if compatibility is required.
*/
template <class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized map through the `deserializer`
* parameter.
*
* The `deserializer` parameter must be a function object that supports the
* following calls:
* - `template<typename U> U operator()();` where the types `std::uint64_t`,
* `float` and `std::pair<Key, T>` must be supported for U.
*
* If the deserialized hash map type is hash compatible with the serialized
* map, the deserialization process can be sped up by setting
* `hash_compatible` to true. To be hash compatible, the Hash and KeyEqual
* must behave the same way than the ones used on the serialized map. The
* `std::size_t` must also be of the same size as the one on the platform used
* to serialize the map, the same apply for `IndexType`. If these criteria are
* not met, the behaviour is undefined with `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `Key` and `T` of the `ordered_map`
* are not the same as the types used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for
* floats, size of int, ...) of the types it deserializes in the hands of the
* `Deserializer` function object if compatibility is required.
*/
template <class Deserializer>
static ordered_map deserialize(Deserializer& deserializer,
bool hash_compatible = false) {
ordered_map map(0);
map.m_ht.deserialize(deserializer, hash_compatible);
return map;
}
friend bool operator==(const ordered_map& lhs, const ordered_map& rhs) {
return lhs.m_ht == rhs.m_ht;
}
friend bool operator!=(const ordered_map& lhs, const ordered_map& rhs) {
return lhs.m_ht != rhs.m_ht;
}
friend bool operator<(const ordered_map& lhs, const ordered_map& rhs) {
return lhs.m_ht < rhs.m_ht;
}
friend bool operator<=(const ordered_map& lhs, const ordered_map& rhs) {
return lhs.m_ht <= rhs.m_ht;
}
friend bool operator>(const ordered_map& lhs, const ordered_map& rhs) {
return lhs.m_ht > rhs.m_ht;
}
friend bool operator>=(const ordered_map& lhs, const ordered_map& rhs) {
return lhs.m_ht >= rhs.m_ht;
}
friend void swap(ordered_map& lhs, ordered_map& rhs) { lhs.swap(rhs); }
private:
ht m_ht;
};
} // end namespace tsl
#endif
+817
View File
@@ -0,0 +1,817 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ORDERED_SET_H
#define TSL_ORDERED_SET_H
#include <cstddef>
#include <cstdint>
#include <deque>
#include <functional>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "ordered_hash.h"
namespace tsl {
/**
* Implementation of an hash set using open addressing with robin hood with
* backshift delete to resolve collisions.
*
* The particularity of this hash set is that it remembers the order in which
* the elements were added and provide a way to access the structure which
* stores these values through the 'values_container()' method. The used
* container is defined by ValueTypeContainer, by default a std::deque is used
* (grows faster) but a std::vector may be used. In this case the set provides a
* 'data()' method which give a direct access to the memory used to store the
* values (which can be useful to communicate with C API's).
*
* The Key must be copy constructible and/or move constructible. To use
* `unordered_erase` it also must be swappable.
*
* The behaviour of the hash set is undefined if the destructor of Key throws an
* exception.
*
* By default the maximum size of a set is limited to 2^32 - 1 values, if needed
* this can be changed through the IndexType template parameter. Using an
* `uint64_t` will raise this limit to 2^64 - 1 values but each bucket will use
* 16 bytes instead of 8 bytes in addition to the space needed to store the
* values.
*
* Iterators invalidation:
* - clear, operator=, reserve, rehash: always invalidate the iterators (also
* invalidate end()).
* - insert, emplace, emplace_hint, operator[]: when a std::vector is used as
* ValueTypeContainer and if size() < capacity(), only end(). Otherwise all the
* iterators are invalidated if an insert occurs.
* - erase, unordered_erase: when a std::vector is used as ValueTypeContainer
* invalidate the iterator of the erased element and all the ones after the
* erased element (including end()). Otherwise all the iterators are invalidated
* if an erase occurs.
*/
template <class Key, class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key>,
class ValueTypeContainer = std::deque<Key, Allocator>,
class IndexType = std::uint_least32_t>
class ordered_set {
private:
template <typename U>
using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent<U>;
class KeySelect {
public:
using key_type = Key;
const key_type& operator()(const Key& key) const noexcept { return key; }
key_type& operator()(Key& key) noexcept { return key; }
};
using ht = detail_ordered_hash::ordered_hash<Key, KeySelect, void, Hash,
KeyEqual, Allocator,
ValueTypeContainer, IndexType>;
public:
using key_type = typename ht::key_type;
using value_type = typename ht::value_type;
using size_type = typename ht::size_type;
using difference_type = typename ht::difference_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using allocator_type = typename ht::allocator_type;
using reference = typename ht::reference;
using const_reference = typename ht::const_reference;
using pointer = typename ht::pointer;
using const_pointer = typename ht::const_pointer;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
using reverse_iterator = typename ht::reverse_iterator;
using const_reverse_iterator = typename ht::const_reverse_iterator;
using values_container_type = typename ht::values_container_type;
/*
* Constructors
*/
ordered_set() : ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {}
explicit ordered_set(size_type bucket_count, const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR) {}
ordered_set(size_type bucket_count, const Allocator& alloc)
: ordered_set(bucket_count, Hash(), KeyEqual(), alloc) {}
ordered_set(size_type bucket_count, const Hash& hash, const Allocator& alloc)
: ordered_set(bucket_count, hash, KeyEqual(), alloc) {}
explicit ordered_set(const Allocator& alloc)
: ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {}
template <class InputIt>
ordered_set(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: ordered_set(bucket_count, hash, equal, alloc) {
insert(first, last);
}
template <class InputIt>
ordered_set(InputIt first, InputIt last, size_type bucket_count,
const Allocator& alloc)
: ordered_set(first, last, bucket_count, Hash(), KeyEqual(), alloc) {}
template <class InputIt>
ordered_set(InputIt first, InputIt last, size_type bucket_count,
const Hash& hash, const Allocator& alloc)
: ordered_set(first, last, bucket_count, hash, KeyEqual(), alloc) {}
ordered_set(std::initializer_list<value_type> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: ordered_set(init.begin(), init.end(), bucket_count, hash, equal,
alloc) {}
ordered_set(std::initializer_list<value_type> init, size_type bucket_count,
const Allocator& alloc)
: ordered_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(),
alloc) {}
ordered_set(std::initializer_list<value_type> init, size_type bucket_count,
const Hash& hash, const Allocator& alloc)
: ordered_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(),
alloc) {}
ordered_set& operator=(std::initializer_list<value_type> ilist) {
m_ht.clear();
m_ht.reserve(ilist.size());
m_ht.insert(ilist.begin(), ilist.end());
return *this;
}
allocator_type get_allocator() const { return m_ht.get_allocator(); }
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
reverse_iterator rbegin() noexcept { return m_ht.rbegin(); }
const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); }
const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); }
reverse_iterator rend() noexcept { return m_ht.rend(); }
const_reverse_iterator rend() const noexcept { return m_ht.rend(); }
const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert(const value_type& value) {
return m_ht.insert(value);
}
std::pair<iterator, bool> insert(value_type&& value) {
return m_ht.insert(std::move(value));
}
iterator insert(const_iterator hint, const value_type& value) {
return m_ht.insert_hint(hint, value);
}
iterator insert(const_iterator hint, value_type&& value) {
return m_ht.insert_hint(hint, std::move(value));
}
template <class InputIt>
void insert(InputIt first, InputIt last) {
m_ht.insert(first, last);
}
void insert(std::initializer_list<value_type> ilist) {
m_ht.insert(ilist.begin(), ilist.end());
}
/**
* Due to the way elements are stored, emplace will need to move or copy the
* key-value once. The method is equivalent to
* insert(value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
return m_ht.emplace(std::forward<Args>(args)...);
}
/**
* Due to the way elements are stored, emplace_hint will need to move or copy
* the key-value once. The method is equivalent to insert(hint,
* value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template <class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) {
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
}
/**
* When erasing an element, the insert order will be preserved and no holes
* will be present in the container returned by 'values_container()'.
*
* The method is in O(bucket_count()), if the order is not important
* 'unordered_erase(...)' method is faster with an O(1) average complexity.
*/
iterator erase(iterator pos) { return m_ht.erase(pos); }
/**
* @copydoc erase(iterator pos)
*/
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
/**
* @copydoc erase(iterator pos)
*/
iterator erase(const_iterator first, const_iterator last) {
return m_ht.erase(first, last);
}
/**
* @copydoc erase(iterator pos)
*/
size_type erase(const key_type& key) { return m_ht.erase(key); }
/**
* @copydoc erase(iterator pos)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup to the value if you already have the hash.
*/
size_type erase(const key_type& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
/**
* @copydoc erase(iterator pos)
*
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key) {
return m_ht.erase(key);
}
/**
* @copydoc erase(const key_type& key, std::size_t precalculated_hash)
*
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
void swap(ordered_set& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
size_type count(const Key& key) const { return m_ht.count(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
size_type count(const Key& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key) const {
return m_ht.count(key);
}
/**
* @copydoc count(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
iterator find(const Key& key) { return m_ht.find(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
iterator find(const Key& key, std::size_t precalculated_hash) {
return m_ht.find(key, precalculated_hash);
}
const_iterator find(const Key& key) const { return m_ht.find(key); }
/**
* @copydoc find(const Key& key, std::size_t precalculated_hash)
*/
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key) {
return m_ht.find(key);
}
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key, std::size_t precalculated_hash) {
return m_ht.find(key, precalculated_hash);
}
/**
* @copydoc find(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key) const {
return m_ht.find(key);
}
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
bool contains(const Key& key) const { return m_ht.contains(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
bool contains(const Key& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key) const {
return m_ht.contains(key);
}
/**
* @copydoc contains(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
std::pair<iterator, iterator> equal_range(const Key& key) {
return m_ht.equal_range(key);
}
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
std::pair<iterator, iterator> equal_range(const Key& key,
std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(
const Key& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key) {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key,
std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* @copydoc equal_range(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key) const {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(
const K& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count) { m_ht.rehash(count); }
void reserve(size_type count) { m_ht.reserve(count); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Convert a const_iterator to an iterator.
*/
iterator mutable_iterator(const_iterator pos) {
return m_ht.mutable_iterator(pos);
}
/**
* Requires index <= size().
*
* Return an iterator to the element at index. Return end() if index ==
* size().
*/
iterator nth(size_type index) { return m_ht.nth(index); }
/**
* @copydoc nth(size_type index)
*/
const_iterator nth(size_type index) const { return m_ht.nth(index); }
/**
* Return const_reference to the first element. Requires the container to not
* be empty.
*/
const_reference front() const { return m_ht.front(); }
/**
* Return const_reference to the last element. Requires the container to not
* be empty.
*/
const_reference back() const { return m_ht.back(); }
/**
* Only available if ValueTypeContainer is a std::vector. Same as calling
* 'values_container().data()'.
*/
template <class U = values_container_type,
typename std::enable_if<
tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
const typename values_container_type::value_type* data() const noexcept {
return m_ht.data();
}
/**
* Return the container in which the values are stored. The values are in the
* same order as the insertion order and are contiguous in the structure, no
* holes (size() == values_container().size()).
*/
const values_container_type& values_container() const noexcept {
return m_ht.values_container();
}
/**
* Release the container in which the values are stored.
*
* The set is empty after this operation.
*/
values_container_type release() { return m_ht.release(); }
template <class U = values_container_type,
typename std::enable_if<
tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
size_type capacity() const noexcept {
return m_ht.capacity();
}
void shrink_to_fit() { m_ht.shrink_to_fit(); }
/**
* Insert the value before pos shifting all the elements on the right of pos
* (including pos) one position to the right.
*
* O(bucket_count()) runtime complexity.
*/
std::pair<iterator, bool> insert_at_position(const_iterator pos,
const value_type& value) {
return m_ht.insert_at_position(pos, value);
}
/**
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
*/
std::pair<iterator, bool> insert_at_position(const_iterator pos,
value_type&& value) {
return m_ht.insert_at_position(pos, std::move(value));
}
/**
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
*
* Same as insert_at_position(pos, value_type(std::forward<Args>(args)...),
* mainly here for coherence.
*/
template <class... Args>
std::pair<iterator, bool> emplace_at_position(const_iterator pos,
Args&&... args) {
return m_ht.emplace_at_position(pos, std::forward<Args>(args)...);
}
void pop_back() { m_ht.pop_back(); }
/**
* Faster erase operation with an O(1) average complexity but it doesn't
* preserve the insertion order.
*
* If an erasure occurs, the last element of the map will take the place of
* the erased element.
*/
iterator unordered_erase(iterator pos) { return m_ht.unordered_erase(pos); }
/**
* @copydoc unordered_erase(iterator pos)
*/
iterator unordered_erase(const_iterator pos) {
return m_ht.unordered_erase(pos);
}
/**
* @copydoc unordered_erase(iterator pos)
*/
size_type unordered_erase(const key_type& key) {
return m_ht.unordered_erase(key);
}
/**
* @copydoc unordered_erase(iterator pos)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
size_type unordered_erase(const key_type& key,
std::size_t precalculated_hash) {
return m_ht.unordered_erase(key, precalculated_hash);
}
/**
* @copydoc unordered_erase(iterator pos)
*
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type unordered_erase(const K& key) {
return m_ht.unordered_erase(key);
}
/**
* @copydoc unordered_erase(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type unordered_erase(const K& key, std::size_t precalculated_hash) {
return m_ht.unordered_erase(key, precalculated_hash);
}
/**
* Serialize the set through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the
* following call:
* - `void operator()(const U& value);` where the types `std::uint64_t`,
* `float` and `Key` must be supported for U.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for
* floats, ...) of the types it serializes in the hands of the `Serializer`
* function object if compatibility is required.
*/
template <class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized set through the `deserializer`
* parameter.
*
* The `deserializer` parameter must be a function object that supports the
* following calls:
* - `template<typename U> U operator()();` where the types `std::uint64_t`,
* `float` and `Key` must be supported for U.
*
* If the deserialized hash set type is hash compatible with the serialized
* set, the deserialization process can be sped up by setting
* `hash_compatible` to true. To be hash compatible, the Hash and KeyEqual
* must behave the same way than the ones used on the serialized map. The
* `std::size_t` must also be of the same size as the one on the platform used
* to serialize the map, the same apply for `IndexType`. If these criteria are
* not met, the behaviour is undefined with `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `Key` of the `ordered_set` is not
* the same as the type used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for
* floats, size of int, ...) of the types it deserializes in the hands of the
* `Deserializer` function object if compatibility is required.
*/
template <class Deserializer>
static ordered_set deserialize(Deserializer& deserializer,
bool hash_compatible = false) {
ordered_set set(0);
set.m_ht.deserialize(deserializer, hash_compatible);
return set;
}
friend bool operator==(const ordered_set& lhs, const ordered_set& rhs) {
return lhs.m_ht == rhs.m_ht;
}
friend bool operator!=(const ordered_set& lhs, const ordered_set& rhs) {
return lhs.m_ht != rhs.m_ht;
}
friend bool operator<(const ordered_set& lhs, const ordered_set& rhs) {
return lhs.m_ht < rhs.m_ht;
}
friend bool operator<=(const ordered_set& lhs, const ordered_set& rhs) {
return lhs.m_ht <= rhs.m_ht;
}
friend bool operator>(const ordered_set& lhs, const ordered_set& rhs) {
return lhs.m_ht > rhs.m_ht;
}
friend bool operator>=(const ordered_set& lhs, const ordered_set& rhs) {
return lhs.m_ht >= rhs.m_ht;
}
friend void swap(ordered_set& lhs, ordered_set& rhs) { lhs.swap(rhs); }
private:
ht m_ht;
};
} // end namespace tsl
#endif