automerge-rb 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (481) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +175 -0
  4. data/ext/automerge_ext/automerge_ext.c +1805 -0
  5. data/ext/automerge_ext/extconf.rb +132 -0
  6. data/lib/automerge/version.rb +6 -0
  7. data/lib/automerge.rb +110 -0
  8. data/rust-toolchain.toml +4 -0
  9. data/vendor/automerge-rust/Cargo.lock +1909 -0
  10. data/vendor/automerge-rust/Cargo.toml +15 -0
  11. data/vendor/automerge-rust/automerge/Cargo.toml +78 -0
  12. data/vendor/automerge-rust/automerge/README.md +5 -0
  13. data/vendor/automerge-rust/automerge/benches/load_save.rs +102 -0
  14. data/vendor/automerge-rust/automerge/benches/map.rs +260 -0
  15. data/vendor/automerge-rust/automerge/benches/range.rs +37 -0
  16. data/vendor/automerge-rust/automerge/benches/sync.rs +95 -0
  17. data/vendor/automerge-rust/automerge/examples/README.md +7 -0
  18. data/vendor/automerge-rust/automerge/examples/quickstart.rs +57 -0
  19. data/vendor/automerge-rust/automerge/examples/watch.rs +94 -0
  20. data/vendor/automerge-rust/automerge/fuzz/Cargo.toml +29 -0
  21. data/vendor/automerge-rust/automerge/fuzz/fuzz_targets/load.rs +37 -0
  22. data/vendor/automerge-rust/automerge/src/autocommit.rs +1286 -0
  23. data/vendor/automerge-rust/automerge/src/automerge/current_state.rs +546 -0
  24. data/vendor/automerge-rust/automerge/src/automerge/tests.rs +2023 -0
  25. data/vendor/automerge-rust/automerge/src/automerge.rs +2071 -0
  26. data/vendor/automerge-rust/automerge/src/autoserde.rs +128 -0
  27. data/vendor/automerge-rust/automerge/src/change.rs +357 -0
  28. data/vendor/automerge-rust/automerge/src/change_graph.rs +1215 -0
  29. data/vendor/automerge-rust/automerge/src/change_queue.rs +46 -0
  30. data/vendor/automerge-rust/automerge/src/clock.rs +206 -0
  31. data/vendor/automerge-rust/automerge/src/columnar/column_range/boolean.rs +83 -0
  32. data/vendor/automerge-rust/automerge/src/columnar/column_range/delta.rs +148 -0
  33. data/vendor/automerge-rust/automerge/src/columnar/column_range/generic/group.rs +138 -0
  34. data/vendor/automerge-rust/automerge/src/columnar/column_range/generic/simple.rs +76 -0
  35. data/vendor/automerge-rust/automerge/src/columnar/column_range/generic.rs +93 -0
  36. data/vendor/automerge-rust/automerge/src/columnar/column_range/key.rs +272 -0
  37. data/vendor/automerge-rust/automerge/src/columnar/column_range/obj_id.rs +202 -0
  38. data/vendor/automerge-rust/automerge/src/columnar/column_range/opid_list.rs +329 -0
  39. data/vendor/automerge-rust/automerge/src/columnar/column_range/raw.rs +38 -0
  40. data/vendor/automerge-rust/automerge/src/columnar/column_range/rle.rs +216 -0
  41. data/vendor/automerge-rust/automerge/src/columnar/column_range/value.rs +547 -0
  42. data/vendor/automerge-rust/automerge/src/columnar/column_range.rs +17 -0
  43. data/vendor/automerge-rust/automerge/src/columnar/encoding/boolean.rs +197 -0
  44. data/vendor/automerge-rust/automerge/src/columnar/encoding/col_error.rs +88 -0
  45. data/vendor/automerge-rust/automerge/src/columnar/encoding/column_decoder.rs +133 -0
  46. data/vendor/automerge-rust/automerge/src/columnar/encoding/decodable_impls.rs +175 -0
  47. data/vendor/automerge-rust/automerge/src/columnar/encoding/delta.rs +96 -0
  48. data/vendor/automerge-rust/automerge/src/columnar/encoding/encodable_impls.rs +200 -0
  49. data/vendor/automerge-rust/automerge/src/columnar/encoding/leb128.rs +82 -0
  50. data/vendor/automerge-rust/automerge/src/columnar/encoding/properties.rs +178 -0
  51. data/vendor/automerge-rust/automerge/src/columnar/encoding/raw.rs +101 -0
  52. data/vendor/automerge-rust/automerge/src/columnar/encoding/rle.rs +239 -0
  53. data/vendor/automerge-rust/automerge/src/columnar/encoding.rs +67 -0
  54. data/vendor/automerge-rust/automerge/src/columnar/splice_error.rs +47 -0
  55. data/vendor/automerge-rust/automerge/src/columnar.rs +14 -0
  56. data/vendor/automerge-rust/automerge/src/convert.rs +112 -0
  57. data/vendor/automerge-rust/automerge/src/cursor.rs +296 -0
  58. data/vendor/automerge-rust/automerge/src/decoding.rs +475 -0
  59. data/vendor/automerge-rust/automerge/src/error.rs +159 -0
  60. data/vendor/automerge-rust/automerge/src/exid.rs +238 -0
  61. data/vendor/automerge-rust/automerge/src/hydrate/list.rs +140 -0
  62. data/vendor/automerge-rust/automerge/src/hydrate/map.rs +132 -0
  63. data/vendor/automerge-rust/automerge/src/hydrate/tests.rs +40 -0
  64. data/vendor/automerge-rust/automerge/src/hydrate/text.rs +89 -0
  65. data/vendor/automerge-rust/automerge/src/hydrate.rs +368 -0
  66. data/vendor/automerge-rust/automerge/src/indexed_cache.rs +113 -0
  67. data/vendor/automerge-rust/automerge/src/iter/doc.rs +603 -0
  68. data/vendor/automerge-rust/automerge/src/iter/keys.rs +93 -0
  69. data/vendor/automerge-rust/automerge/src/iter/list_range.rs +433 -0
  70. data/vendor/automerge-rust/automerge/src/iter/map_range.rs +316 -0
  71. data/vendor/automerge-rust/automerge/src/iter/spans.rs +601 -0
  72. data/vendor/automerge-rust/automerge/src/iter/tools.rs +427 -0
  73. data/vendor/automerge-rust/automerge/src/iter/values.rs +36 -0
  74. data/vendor/automerge-rust/automerge/src/iter.rs +25 -0
  75. data/vendor/automerge-rust/automerge/src/legacy/mod.rs +364 -0
  76. data/vendor/automerge-rust/automerge/src/legacy/serde_impls/actor_id.rs +25 -0
  77. data/vendor/automerge-rust/automerge/src/legacy/serde_impls/change_hash.rs +29 -0
  78. data/vendor/automerge-rust/automerge/src/legacy/serde_impls/element_id.rs +27 -0
  79. data/vendor/automerge-rust/automerge/src/legacy/serde_impls/mod.rs +31 -0
  80. data/vendor/automerge-rust/automerge/src/legacy/serde_impls/object_id.rs +36 -0
  81. data/vendor/automerge-rust/automerge/src/legacy/serde_impls/op.rs +668 -0
  82. data/vendor/automerge-rust/automerge/src/legacy/serde_impls/op_type.rs +26 -0
  83. data/vendor/automerge-rust/automerge/src/legacy/serde_impls/opid.rs +25 -0
  84. data/vendor/automerge-rust/automerge/src/legacy/serde_impls/scalar_value.rs +63 -0
  85. data/vendor/automerge-rust/automerge/src/legacy/utility_impls/element_id.rs +66 -0
  86. data/vendor/automerge-rust/automerge/src/legacy/utility_impls/key.rs +49 -0
  87. data/vendor/automerge-rust/automerge/src/legacy/utility_impls/mod.rs +4 -0
  88. data/vendor/automerge-rust/automerge/src/legacy/utility_impls/object_id.rs +74 -0
  89. data/vendor/automerge-rust/automerge/src/legacy/utility_impls/opid.rs +68 -0
  90. data/vendor/automerge-rust/automerge/src/lib.rs +315 -0
  91. data/vendor/automerge-rust/automerge/src/marks.rs +478 -0
  92. data/vendor/automerge-rust/automerge/src/op_set2/change/batch.rs +2002 -0
  93. data/vendor/automerge-rust/automerge/src/op_set2/change/collector.rs +974 -0
  94. data/vendor/automerge-rust/automerge/src/op_set2/change.rs +332 -0
  95. data/vendor/automerge-rust/automerge/src/op_set2/columns.rs +714 -0
  96. data/vendor/automerge-rust/automerge/src/op_set2/meta.rs +174 -0
  97. data/vendor/automerge-rust/automerge/src/op_set2/op.rs +1363 -0
  98. data/vendor/automerge-rust/automerge/src/op_set2/op_set/elems.rs +43 -0
  99. data/vendor/automerge-rust/automerge/src/op_set2/op_set/found_op.rs +60 -0
  100. data/vendor/automerge-rust/automerge/src/op_set2/op_set/index.rs +197 -0
  101. data/vendor/automerge-rust/automerge/src/op_set2/op_set/insert.rs +179 -0
  102. data/vendor/automerge-rust/automerge/src/op_set2/op_set/mark_index.rs +292 -0
  103. data/vendor/automerge-rust/automerge/src/op_set2/op_set/marks.rs +86 -0
  104. data/vendor/automerge-rust/automerge/src/op_set2/op_set/op_iter.rs +1295 -0
  105. data/vendor/automerge-rust/automerge/src/op_set2/op_set/op_query.rs +82 -0
  106. data/vendor/automerge-rust/automerge/src/op_set2/op_set/top_op.rs +50 -0
  107. data/vendor/automerge-rust/automerge/src/op_set2/op_set/visible.rs +290 -0
  108. data/vendor/automerge-rust/automerge/src/op_set2/op_set.rs +1793 -0
  109. data/vendor/automerge-rust/automerge/src/op_set2/parents.rs +133 -0
  110. data/vendor/automerge-rust/automerge/src/op_set2/skip_list.rs +714 -0
  111. data/vendor/automerge-rust/automerge/src/op_set2/types.rs +769 -0
  112. data/vendor/automerge-rust/automerge/src/op_set2.rs +18 -0
  113. data/vendor/automerge-rust/automerge/src/patches/patch.rs +95 -0
  114. data/vendor/automerge-rust/automerge/src/patches/patch_builder.rs +382 -0
  115. data/vendor/automerge-rust/automerge/src/patches/patch_log.rs +584 -0
  116. data/vendor/automerge-rust/automerge/src/patches.rs +7 -0
  117. data/vendor/automerge-rust/automerge/src/query/list_state.rs +230 -0
  118. data/vendor/automerge-rust/automerge/src/query/seek_mark.rs +142 -0
  119. data/vendor/automerge-rust/automerge/src/read.rs +326 -0
  120. data/vendor/automerge-rust/automerge/src/sequence_tree.rs +662 -0
  121. data/vendor/automerge-rust/automerge/src/storage/bundle/builder.rs +942 -0
  122. data/vendor/automerge-rust/automerge/src/storage/bundle/error.rs +42 -0
  123. data/vendor/automerge-rust/automerge/src/storage/bundle/meta.rs +23 -0
  124. data/vendor/automerge-rust/automerge/src/storage/bundle/storage.rs +146 -0
  125. data/vendor/automerge-rust/automerge/src/storage/bundle.rs +210 -0
  126. data/vendor/automerge-rust/automerge/src/storage/change/change_actors.rs +311 -0
  127. data/vendor/automerge-rust/automerge/src/storage/change/change_op_columns.rs +621 -0
  128. data/vendor/automerge-rust/automerge/src/storage/change/compressed.rs +51 -0
  129. data/vendor/automerge-rust/automerge/src/storage/change/op_with_change_actors.rs +1 -0
  130. data/vendor/automerge-rust/automerge/src/storage/change.rs +523 -0
  131. data/vendor/automerge-rust/automerge/src/storage/chunk.rs +312 -0
  132. data/vendor/automerge-rust/automerge/src/storage/columns/column.rs +42 -0
  133. data/vendor/automerge-rust/automerge/src/storage/columns/column_builder.rs +199 -0
  134. data/vendor/automerge-rust/automerge/src/storage/columns/column_specification.rs +340 -0
  135. data/vendor/automerge-rust/automerge/src/storage/columns/raw_column.rs +286 -0
  136. data/vendor/automerge-rust/automerge/src/storage/columns.rs +355 -0
  137. data/vendor/automerge-rust/automerge/src/storage/document/compression.rs +362 -0
  138. data/vendor/automerge-rust/automerge/src/storage/document.rs +411 -0
  139. data/vendor/automerge-rust/automerge/src/storage/load/change_collector.rs +15 -0
  140. data/vendor/automerge-rust/automerge/src/storage/load.rs +136 -0
  141. data/vendor/automerge-rust/automerge/src/storage/parse/leb128.rs +302 -0
  142. data/vendor/automerge-rust/automerge/src/storage/parse.rs +619 -0
  143. data/vendor/automerge-rust/automerge/src/storage/save/document.rs +27 -0
  144. data/vendor/automerge-rust/automerge/src/storage.rs +26 -0
  145. data/vendor/automerge-rust/automerge/src/sync/bloom.rs +161 -0
  146. data/vendor/automerge-rust/automerge/src/sync/message_builder.rs +118 -0
  147. data/vendor/automerge-rust/automerge/src/sync/state.rs +214 -0
  148. data/vendor/automerge-rust/automerge/src/sync/v1_compat_test/bloom.rs +162 -0
  149. data/vendor/automerge-rust/automerge/src/sync/v1_compat_test/mod.rs +625 -0
  150. data/vendor/automerge-rust/automerge/src/sync/v1_compat_test/state.rs +120 -0
  151. data/vendor/automerge-rust/automerge/src/sync.rs +2482 -0
  152. data/vendor/automerge-rust/automerge/src/text_diff/LICENSE +201 -0
  153. data/vendor/automerge-rust/automerge/src/text_diff/myers.rs +332 -0
  154. data/vendor/automerge-rust/automerge/src/text_diff/replace.rs +139 -0
  155. data/vendor/automerge-rust/automerge/src/text_diff/utils.rs +119 -0
  156. data/vendor/automerge-rust/automerge/src/text_diff.rs +515 -0
  157. data/vendor/automerge-rust/automerge/src/text_value.rs +276 -0
  158. data/vendor/automerge-rust/automerge/src/transaction/commit.rs +34 -0
  159. data/vendor/automerge-rust/automerge/src/transaction/inner.rs +1403 -0
  160. data/vendor/automerge-rust/automerge/src/transaction/manual_transaction.rs +147 -0
  161. data/vendor/automerge-rust/automerge/src/transaction/owned_transaction.rs +266 -0
  162. data/vendor/automerge-rust/automerge/src/transaction/result.rs +21 -0
  163. data/vendor/automerge-rust/automerge/src/transaction/transactable.rs +203 -0
  164. data/vendor/automerge-rust/automerge/src/transaction.rs +513 -0
  165. data/vendor/automerge-rust/automerge/src/types.rs +749 -0
  166. data/vendor/automerge-rust/automerge/src/validation.rs +29 -0
  167. data/vendor/automerge-rust/automerge/src/value.rs +763 -0
  168. data/vendor/automerge-rust/automerge/tests/batch_insert.rs +1034 -0
  169. data/vendor/automerge-rust/automerge/tests/block_tests.rs +887 -0
  170. data/vendor/automerge-rust/automerge/tests/convert_string_to_text.rs +72 -0
  171. data/vendor/automerge-rust/automerge/tests/diff_marks.rs +1508 -0
  172. data/vendor/automerge-rust/automerge/tests/fixtures/64bit_obj_id_change.automerge +0 -0
  173. data/vendor/automerge-rust/automerge/tests/fixtures/64bit_obj_id_doc.automerge +0 -0
  174. data/vendor/automerge-rust/automerge/tests/fixtures/counter_value_has_incorrect_meta.automerge +0 -0
  175. data/vendor/automerge-rust/automerge/tests/fixtures/counter_value_is_ok.automerge +0 -0
  176. data/vendor/automerge-rust/automerge/tests/fixtures/counter_value_is_overlong.automerge +0 -0
  177. data/vendor/automerge-rust/automerge/tests/fixtures/two_change_chunks.automerge +0 -0
  178. data/vendor/automerge-rust/automerge/tests/fixtures/two_change_chunks_compressed.automerge +0 -0
  179. data/vendor/automerge-rust/automerge/tests/fixtures/two_change_chunks_out_of_order.automerge +0 -0
  180. data/vendor/automerge-rust/automerge/tests/fuzz-crashers/action-is-48.automerge +0 -0
  181. data/vendor/automerge-rust/automerge/tests/fuzz-crashers/crash-da39a3ee5e6b4b0d3255bfef95601890afd80709 +0 -0
  182. data/vendor/automerge-rust/automerge/tests/fuzz-crashers/incorrect_max_op.automerge +0 -0
  183. data/vendor/automerge-rust/automerge/tests/fuzz-crashers/invalid_deflate_stream.automerge +0 -0
  184. data/vendor/automerge-rust/automerge/tests/fuzz-crashers/missing_actor.automerge +0 -0
  185. data/vendor/automerge-rust/automerge/tests/fuzz-crashers/overflow_in_length.automerge +0 -0
  186. data/vendor/automerge-rust/automerge/tests/fuzz-crashers/too_many_deps.automerge +0 -0
  187. data/vendor/automerge-rust/automerge/tests/fuzz-crashers/too_many_ops.automerge +0 -0
  188. data/vendor/automerge-rust/automerge/tests/test.rs +2668 -0
  189. data/vendor/automerge-rust/automerge/tests/test_mark_patches.rs +41 -0
  190. data/vendor/automerge-rust/automerge/tests/test_save_load_orphans.rs +84 -0
  191. data/vendor/automerge-rust/automerge/tests/text.rs +1098 -0
  192. data/vendor/automerge-rust/automerge/tests/text_encoding.rs +640 -0
  193. data/vendor/automerge-rust/automerge-c/CMakeLists.txt +439 -0
  194. data/vendor/automerge-rust/automerge-c/Cargo.toml +22 -0
  195. data/vendor/automerge-rust/automerge-c/README.md +233 -0
  196. data/vendor/automerge-rust/automerge-c/build.rs +21 -0
  197. data/vendor/automerge-rust/automerge-c/cbindgen.toml +48 -0
  198. data/vendor/automerge-rust/automerge-c/cmake/Cargo.toml.in +22 -0
  199. data/vendor/automerge-rust/automerge-c/cmake/automerge-c-config.cmake.in +99 -0
  200. data/vendor/automerge-rust/automerge-c/cmake/cbindgen.toml.in +48 -0
  201. data/vendor/automerge-rust/automerge-c/cmake/config.h.in +58 -0
  202. data/vendor/automerge-rust/automerge-c/cmake/enum-string-functions-gen.cmake +183 -0
  203. data/vendor/automerge-rust/automerge-c/cmake/file-regex-replace.cmake +33 -0
  204. data/vendor/automerge-rust/automerge-c/cmake/file-touch.cmake +35 -0
  205. data/vendor/automerge-rust/automerge-c/docs/CMakeLists.txt +39 -0
  206. data/vendor/automerge-rust/automerge-c/docs/img/brandmark.png +0 -0
  207. data/vendor/automerge-rust/automerge-c/examples/CMakeLists.txt +42 -0
  208. data/vendor/automerge-rust/automerge-c/examples/README.md +9 -0
  209. data/vendor/automerge-rust/automerge-c/examples/quickstart.c +131 -0
  210. data/vendor/automerge-rust/automerge-c/include/automerge-c/utils/result.h +30 -0
  211. data/vendor/automerge-rust/automerge-c/include/automerge-c/utils/stack.h +130 -0
  212. data/vendor/automerge-rust/automerge-c/include/automerge-c/utils/stack_callback_data.h +53 -0
  213. data/vendor/automerge-rust/automerge-c/include/automerge-c/utils/string.h +29 -0
  214. data/vendor/automerge-rust/automerge-c/src/actor_id.rs +193 -0
  215. data/vendor/automerge-rust/automerge-c/src/byte_span.rs +227 -0
  216. data/vendor/automerge-rust/automerge-c/src/change.rs +356 -0
  217. data/vendor/automerge-rust/automerge-c/src/cursor.rs +168 -0
  218. data/vendor/automerge-rust/automerge-c/src/doc/list.rs +636 -0
  219. data/vendor/automerge-rust/automerge-c/src/doc/map.rs +556 -0
  220. data/vendor/automerge-rust/automerge-c/src/doc/mark.rs +296 -0
  221. data/vendor/automerge-rust/automerge-c/src/doc/utils.rs +46 -0
  222. data/vendor/automerge-rust/automerge-c/src/doc.rs +1009 -0
  223. data/vendor/automerge-rust/automerge-c/src/index.rs +84 -0
  224. data/vendor/automerge-rust/automerge-c/src/item.rs +2177 -0
  225. data/vendor/automerge-rust/automerge-c/src/items.rs +401 -0
  226. data/vendor/automerge-rust/automerge-c/src/lib.rs +11 -0
  227. data/vendor/automerge-rust/automerge-c/src/obj.rs +216 -0
  228. data/vendor/automerge-rust/automerge-c/src/result.rs +653 -0
  229. data/vendor/automerge-rust/automerge-c/src/sync/have.rs +42 -0
  230. data/vendor/automerge-rust/automerge-c/src/sync/message.rs +146 -0
  231. data/vendor/automerge-rust/automerge-c/src/sync/state.rs +262 -0
  232. data/vendor/automerge-rust/automerge-c/src/sync.rs +7 -0
  233. data/vendor/automerge-rust/automerge-c/src/utils/result.c +33 -0
  234. data/vendor/automerge-rust/automerge-c/src/utils/stack.c +106 -0
  235. data/vendor/automerge-rust/automerge-c/src/utils/stack_callback_data.c +9 -0
  236. data/vendor/automerge-rust/automerge-c/src/utils/string.c +46 -0
  237. data/vendor/automerge-rust/automerge-c/test/CMakeLists.txt +67 -0
  238. data/vendor/automerge-rust/automerge-c/test/actor_id_tests.c +140 -0
  239. data/vendor/automerge-rust/automerge-c/test/base_state.c +17 -0
  240. data/vendor/automerge-rust/automerge-c/test/base_state.h +39 -0
  241. data/vendor/automerge-rust/automerge-c/test/byte_span_tests.c +119 -0
  242. data/vendor/automerge-rust/automerge-c/test/cmocka_utils.c +91 -0
  243. data/vendor/automerge-rust/automerge-c/test/cmocka_utils.h +42 -0
  244. data/vendor/automerge-rust/automerge-c/test/cursor_tests.c +263 -0
  245. data/vendor/automerge-rust/automerge-c/test/doc_state.c +27 -0
  246. data/vendor/automerge-rust/automerge-c/test/doc_state.h +17 -0
  247. data/vendor/automerge-rust/automerge-c/test/doc_tests.c +335 -0
  248. data/vendor/automerge-rust/automerge-c/test/enum_string_tests.c +148 -0
  249. data/vendor/automerge-rust/automerge-c/test/files/brave-ape-49.automerge +0 -0
  250. data/vendor/automerge-rust/automerge-c/test/item_tests.c +313 -0
  251. data/vendor/automerge-rust/automerge-c/test/list_tests.c +544 -0
  252. data/vendor/automerge-rust/automerge-c/test/macro_utils.c +38 -0
  253. data/vendor/automerge-rust/automerge-c/test/macro_utils.h +23 -0
  254. data/vendor/automerge-rust/automerge-c/test/main.c +33 -0
  255. data/vendor/automerge-rust/automerge-c/test/map_tests.c +1610 -0
  256. data/vendor/automerge-rust/automerge-c/test/mark_tests.c +124 -0
  257. data/vendor/automerge-rust/automerge-c/test/ported_wasm/basic_tests.c +1642 -0
  258. data/vendor/automerge-rust/automerge-c/test/ported_wasm/cursor_tests.c +108 -0
  259. data/vendor/automerge-rust/automerge-c/test/ported_wasm/suite.c +17 -0
  260. data/vendor/automerge-rust/automerge-c/test/ported_wasm/sync_tests.c +1280 -0
  261. data/vendor/automerge-rust/automerge-c/test/str_utils.c +15 -0
  262. data/vendor/automerge-rust/automerge-c/test/str_utils.h +17 -0
  263. data/vendor/automerge-rust/automerge-test/Cargo.toml +17 -0
  264. data/vendor/automerge-rust/automerge-test/README.md +3 -0
  265. data/vendor/automerge-rust/automerge-test/src/lib.rs +487 -0
  266. data/vendor/automerge-rust/hexane/CHANGELOG.md +34 -0
  267. data/vendor/automerge-rust/hexane/Cargo.toml +47 -0
  268. data/vendor/automerge-rust/hexane/README.md +292 -0
  269. data/vendor/automerge-rust/hexane/RESULTS.txt +20 -0
  270. data/vendor/automerge-rust/hexane/TODO +18 -0
  271. data/vendor/automerge-rust/hexane/benches/insert.rs +82 -0
  272. data/vendor/automerge-rust/hexane/benches/seek.rs +77 -0
  273. data/vendor/automerge-rust/hexane/benches/splice.rs +82 -0
  274. data/vendor/automerge-rust/hexane/src/aggregate.rs +288 -0
  275. data/vendor/automerge-rust/hexane/src/boolean.rs +478 -0
  276. data/vendor/automerge-rust/hexane/src/columndata.rs +2540 -0
  277. data/vendor/automerge-rust/hexane/src/cursor.rs +756 -0
  278. data/vendor/automerge-rust/hexane/src/delta.rs +793 -0
  279. data/vendor/automerge-rust/hexane/src/encoder.rs +639 -0
  280. data/vendor/automerge-rust/hexane/src/leb128.rs +82 -0
  281. data/vendor/automerge-rust/hexane/src/lib.rs +95 -0
  282. data/vendor/automerge-rust/hexane/src/pack.rs +325 -0
  283. data/vendor/automerge-rust/hexane/src/raw.rs +314 -0
  284. data/vendor/automerge-rust/hexane/src/rle.rs +928 -0
  285. data/vendor/automerge-rust/hexane/src/slab/tree.rs +1373 -0
  286. data/vendor/automerge-rust/hexane/src/slab/writer.rs +535 -0
  287. data/vendor/automerge-rust/hexane/src/slab.rs +224 -0
  288. data/vendor/automerge-rust/hexane/src/test.rs +108 -0
  289. data/vendor/bundle/ruby/3.3.0/bin/rake +29 -0
  290. data/vendor/bundle/ruby/3.3.0/bin/rake-compiler +29 -0
  291. data/vendor/bundle/ruby/3.3.0/bin/rake-compiler-dock +29 -0
  292. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/History.rdoc +1732 -0
  293. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/Manifest.txt +32 -0
  294. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/README.rdoc +845 -0
  295. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/Rakefile +97 -0
  296. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/design_rationale.rb +54 -0
  297. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/hoe/minitest.rb +30 -0
  298. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/assertions.rb +850 -0
  299. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/autorun.rb +6 -0
  300. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/benchmark.rb +452 -0
  301. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/compress.rb +94 -0
  302. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/error_on_warning.rb +11 -0
  303. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/expectations.rb +321 -0
  304. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/hell.rb +11 -0
  305. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/manual_plugins.rb +16 -0
  306. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/mock.rb +327 -0
  307. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/parallel.rb +72 -0
  308. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/pride.rb +4 -0
  309. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/pride_plugin.rb +135 -0
  310. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/spec.rb +353 -0
  311. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/test.rb +238 -0
  312. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/test_task.rb +324 -0
  313. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest/unit.rb +42 -0
  314. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/lib/minitest.rb +1250 -0
  315. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/metametameta.rb +150 -0
  316. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_assertions.rb +1677 -0
  317. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_benchmark.rb +137 -0
  318. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_mock.rb +1213 -0
  319. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_reporter.rb +437 -0
  320. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_spec.rb +1159 -0
  321. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_test.rb +1374 -0
  322. data/vendor/bundle/ruby/3.3.0/gems/minitest-5.27.0/test/minitest/test_minitest_test_task.rb +57 -0
  323. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/History.rdoc +2454 -0
  324. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/MIT-LICENSE +21 -0
  325. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/README.rdoc +155 -0
  326. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/command_line_usage.rdoc +171 -0
  327. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/Rakefile1 +38 -0
  328. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/Rakefile2 +35 -0
  329. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/a.c +6 -0
  330. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/b.c +6 -0
  331. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/example/main.c +11 -0
  332. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/glossary.rdoc +42 -0
  333. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/jamis.rb +592 -0
  334. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/proto_rake.rdoc +127 -0
  335. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/rake.1 +156 -0
  336. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/rakefile.rdoc +635 -0
  337. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/doc/rational.rdoc +151 -0
  338. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/exe/rake +27 -0
  339. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/application.rb +847 -0
  340. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/backtrace.rb +25 -0
  341. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/clean.rb +78 -0
  342. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/cloneable.rb +17 -0
  343. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/cpu_counter.rb +122 -0
  344. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/default_loader.rb +15 -0
  345. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/dsl_definition.rb +196 -0
  346. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/early_time.rb +22 -0
  347. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/ext/core.rb +26 -0
  348. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/ext/string.rb +176 -0
  349. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_creation_task.rb +25 -0
  350. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_list.rb +435 -0
  351. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_task.rb +58 -0
  352. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_utils.rb +137 -0
  353. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/file_utils_ext.rb +135 -0
  354. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/invocation_chain.rb +57 -0
  355. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/invocation_exception_mixin.rb +17 -0
  356. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/late_time.rb +18 -0
  357. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/linked_list.rb +112 -0
  358. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/loaders/makefile.rb +54 -0
  359. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/multi_task.rb +14 -0
  360. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/name_space.rb +38 -0
  361. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/options.rb +31 -0
  362. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/packagetask.rb +222 -0
  363. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/phony.rb +16 -0
  364. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/private_reader.rb +21 -0
  365. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/promise.rb +100 -0
  366. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/pseudo_status.rb +30 -0
  367. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/rake_module.rb +67 -0
  368. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/rake_test_loader.rb +27 -0
  369. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/rule_recursion_overflow_error.rb +20 -0
  370. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/scope.rb +43 -0
  371. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/task.rb +434 -0
  372. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/task_argument_error.rb +8 -0
  373. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/task_arguments.rb +113 -0
  374. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/task_manager.rb +333 -0
  375. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/tasklib.rb +12 -0
  376. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/testtask.rb +192 -0
  377. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/thread_history_display.rb +49 -0
  378. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/thread_pool.rb +157 -0
  379. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/trace_output.rb +23 -0
  380. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/version.rb +10 -0
  381. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake/win32.rb +17 -0
  382. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/lib/rake.rb +69 -0
  383. data/vendor/bundle/ruby/3.3.0/gems/rake-13.4.2/rake.gemspec +102 -0
  384. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/Gemfile +8 -0
  385. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/History.md +695 -0
  386. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/LICENSE.txt +20 -0
  387. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/README.md +476 -0
  388. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/Rakefile +15 -0
  389. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/appveyor.yml +22 -0
  390. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/bin/rake-compiler +24 -0
  391. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/cucumber.yml +4 -0
  392. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/compile.feature +79 -0
  393. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/cross-compile.feature +23 -0
  394. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/cross-package-multi.feature +15 -0
  395. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/cross-package.feature +14 -0
  396. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/java-compile.feature +22 -0
  397. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/java-no-native-compile.feature +33 -0
  398. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/java-package.feature +24 -0
  399. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/package.feature +40 -0
  400. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/compilation.rb +70 -0
  401. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/cross_compilation.rb +27 -0
  402. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/execution.rb +52 -0
  403. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/folders.rb +32 -0
  404. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/gem.rb +46 -0
  405. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/step_definitions/java_compilation.rb +7 -0
  406. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/support/env.rb +10 -0
  407. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/support/file_template_helpers.rb +137 -0
  408. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/support/generator_helpers.rb +123 -0
  409. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/features/support/platform_extension_helpers.rb +27 -0
  410. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/baseextensiontask.rb +90 -0
  411. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/compiler_config.rb +38 -0
  412. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/extensioncompiler.rb +51 -0
  413. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/extensiontask.rb +589 -0
  414. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/lib/rake/javaextensiontask.rb +321 -0
  415. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/bin/cross-ruby.rake +189 -0
  416. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/bootstrap.rake +11 -0
  417. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/common.rake +10 -0
  418. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/cucumber.rake +23 -0
  419. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/gem.rake +15 -0
  420. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-1.3.1/tasks/rspec.rake +9 -0
  421. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/CHANGELOG.md +446 -0
  422. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/CONTRIBUTING.md +109 -0
  423. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/Dockerfile.jruby +79 -0
  424. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/Dockerfile.mri.erb +282 -0
  425. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/Gemfile +8 -0
  426. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/LICENSE.txt +22 -0
  427. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/README.md +447 -0
  428. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/Rakefile +246 -0
  429. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/bin/rake-compiler-dock +18 -0
  430. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/buildkitd.toml +2 -0
  431. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/gem_helper.rb +54 -0
  432. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/mk_i686.rb +18 -0
  433. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/mk_musl_cross.sh +37 -0
  434. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/mk_osxcross.sh +45 -0
  435. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/mk_pkg_config.sh +24 -0
  436. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/parallel_docker_build.rb +169 -0
  437. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/patches/rake-compiler-1.3.1/0004-Enable-build-of-static-libruby.patch +38 -0
  438. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/patches/rake-compiler-1.3.1/0005-build-miniruby-first.patch +16 -0
  439. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/patches/rake-compiler-1.3.1/0006-ruby-4-rubyspec-capiext.patch +16 -0
  440. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/rcd-env.sh +6 -0
  441. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/runas +7 -0
  442. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/sigfw.c +45 -0
  443. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/strip_wrapper_codesign +17 -0
  444. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/strip_wrapper_vbox +30 -0
  445. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/build/sudoers +1 -0
  446. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/colors.rb +43 -0
  447. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/docker_check.rb +356 -0
  448. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/predefined_user_group.rb +5 -0
  449. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/starter.rb +206 -0
  450. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/version.rb +4 -0
  451. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock.rb +151 -0
  452. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/Dockerfile +66 -0
  453. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/README.md +14 -0
  454. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/binutils-mingw-w64-ignore-check-errors.patch +13 -0
  455. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/gcc-mingw-w64-only-c-c++.patch +13 -0
  456. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/mingw-w64-enable-ucrt.patch +22 -0
  457. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/rake-compiler-dock.gemspec +34 -0
  458. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/env/Dockerfile.alpine +17 -0
  459. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/env/Dockerfile.debian +24 -0
  460. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/fixtures/mig_test_rpc.defs +8 -0
  461. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/Gemfile +11 -0
  462. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/Rakefile +97 -0
  463. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/java/RcdTestExtService.java +19 -0
  464. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/java/RubyRcdTest.java +16 -0
  465. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/mri/extconf.rb +111 -0
  466. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/mri/rcd_test_ext.c +65 -0
  467. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/mri/rcd_test_ext.h +11 -0
  468. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/lib/rcd_test.rb +6 -0
  469. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/rcd_test.gemspec +28 -0
  470. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/test/test_basic.rb +49 -0
  471. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_environment_variables.rb +108 -0
  472. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_mig.rb +18 -0
  473. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_parallel_docker_build.rb +95 -0
  474. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_rubygems_plugins.rb +12 -0
  475. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_starter.rb +158 -0
  476. data/vendor/bundle/ruby/3.3.0/gems/rake-compiler-dock-1.12.0/test/test_versions.rb +82 -0
  477. data/vendor/bundle/ruby/3.3.0/specifications/minitest-5.27.0.gemspec +32 -0
  478. data/vendor/bundle/ruby/3.3.0/specifications/rake-13.4.2.gemspec +26 -0
  479. data/vendor/bundle/ruby/3.3.0/specifications/rake-compiler-1.3.1.gemspec +33 -0
  480. data/vendor/bundle/ruby/3.3.0/specifications/rake-compiler-dock-1.12.0.gemspec +28 -0
  481. metadata +584 -0
@@ -0,0 +1,2482 @@
1
+ //! # Sync Protocol
2
+ //!
3
+ //! The sync protocol is based on this paper:
4
+ //! <https://arxiv.org/abs/2012.00472>, it assumes a reliable in-order stream
5
+ //! between two peers who are synchronizing a document.
6
+ //!
7
+ //! Each peer maintains a [`State`] for each peer they are synchronizing with.
8
+ //! This state tracks things like what the heads of the other peer are and
9
+ //! whether there are in-flight messages. Anything which implements [`SyncDoc`]
10
+ //! can take part in the sync protocol. The flow goes something like this:
11
+ //!
12
+ //! * The initiating peer creates an empty [`State`] and then calls
13
+ //! [`SyncDoc::generate_sync_message()`] to generate new sync message and sends
14
+ //! it to the receiving peer.
15
+ //! * The receiving peer receives a message from the initiator, creates a new
16
+ //! [`State`], and calls [`SyncDoc::receive_sync_message()`] on it's view of the
17
+ //! document
18
+ //! * The receiving peer then calls [`SyncDoc::generate_sync_message()`] to generate
19
+ //! a new sync message and send it back to the initiator
20
+ //! * From this point on each peer operates in a loop, receiving a sync message
21
+ //! from the other peer and then generating a new message to send back.
22
+ //!
23
+ //! ## Example
24
+ //!
25
+ //! ```
26
+ //! use automerge::{transaction::Transactable, sync::{self, SyncDoc}, ReadDoc};
27
+ //! # fn main() -> Result<(), automerge::AutomergeError> {
28
+ //! // Create a document on peer1
29
+ //! let mut peer1 = automerge::AutoCommit::new();
30
+ //! peer1.put(automerge::ROOT, "key", "value")?;
31
+ //!
32
+ //! // Create a state to track our sync with peer2
33
+ //! let mut peer1_state = sync::State::new();
34
+ //! // Generate the initial message to send to peer2, unwrap for brevity
35
+ //! let message1to2 = peer1.sync().generate_sync_message(&mut peer1_state).unwrap();
36
+ //!
37
+ //! // We receive the message on peer2. We don't have a document at all yet
38
+ //! // so we create one
39
+ //! let mut peer2 = automerge::AutoCommit::new();
40
+ //! // We don't have a state for peer1 (it's a new connection), so we create one
41
+ //! let mut peer2_state = sync::State::new();
42
+ //! // Now receive the message from peer 1
43
+ //! peer2.sync().receive_sync_message(&mut peer2_state, message1to2)?;
44
+ //!
45
+ //! // Now we loop, sending messages from one to two and two to one until
46
+ //! // neither has anything new to send
47
+ //!
48
+ //! loop {
49
+ //! let two_to_one = peer2.sync().generate_sync_message(&mut peer2_state);
50
+ //! if let Some(message) = two_to_one.as_ref() {
51
+ //! println!("two to one");
52
+ //! peer1.sync().receive_sync_message(&mut peer1_state, message.clone())?;
53
+ //! }
54
+ //! let one_to_two = peer1.sync().generate_sync_message(&mut peer1_state);
55
+ //! if let Some(message) = one_to_two.as_ref() {
56
+ //! println!("one to two");
57
+ //! peer2.sync().receive_sync_message(&mut peer2_state, message.clone())?;
58
+ //! }
59
+ //! if two_to_one.is_none() && one_to_two.is_none() {
60
+ //! break;
61
+ //! }
62
+ //! }
63
+ //!
64
+ //! assert_eq!(peer2.get(automerge::ROOT, "key")?.unwrap().0.to_str(), Some("value"));
65
+ //!
66
+ //! # Ok(())
67
+ //! # }
68
+ //! ```
69
+
70
+ use itertools::Itertools;
71
+ use serde::ser::SerializeMap;
72
+ use std::collections::{HashMap, HashSet};
73
+
74
+ use crate::{
75
+ patches::PatchLog,
76
+ storage::{parse, ReadChangeOpError},
77
+ Automerge, AutomergeError, ChangeHash, ReadDoc,
78
+ };
79
+
80
+ mod bloom;
81
+ mod message_builder;
82
+ mod state;
83
+ use message_builder::MessageBuilder;
84
+
85
+ #[cfg(test)]
86
+ mod v1_compat_test;
87
+
88
+ pub use bloom::{BloomFilter, DecodeError as DecodeBloomError};
89
+ pub use state::DecodeError as DecodeStateError;
90
+ pub use state::{Have, State};
91
+
92
+ /// A document which can take part in the sync protocol
93
+ ///
94
+ /// See the [module level documentation](crate::sync) for more details.
95
+ pub trait SyncDoc {
96
+ /// Generate a sync message for the remote peer represented by `sync_state`
97
+ ///
98
+ /// If this returns [`None`] then there are no new messages to send, either because we are
99
+ /// waiting for an acknolwedgement of an in-flight message, or because the remote is up to
100
+ /// date.
101
+ ///
102
+ /// * `sync_state` - The [`State`] for this document and the remote peer
103
+ /// * `message` - The [`Message`] to receive
104
+ /// * `patch_log` - A [`PatchLog`] which will be updated with any changes that are made to the current state of the document due to the received sync message
105
+ fn generate_sync_message(&self, sync_state: &mut State) -> Option<Message>;
106
+
107
+ /// Apply a received sync message to this document and `sync_state`
108
+ fn receive_sync_message(
109
+ &mut self,
110
+ sync_state: &mut State,
111
+ message: Message,
112
+ ) -> Result<(), AutomergeError>;
113
+
114
+ /// Apply a received sync message to this document and `sync_state`, logging any changes that
115
+ /// are made to `patch_log`
116
+ ///
117
+ /// If this returns [`None`] then there are no new messages to send, either because we are
118
+ /// waiting for an acknolwedgement of an in-flight message, or because the remote is up to
119
+ /// date.
120
+ ///
121
+ /// # Arguments
122
+ ///
123
+ /// * `sync_state` - The [`State`] for this document and the remote peer
124
+ /// * `message` - The [`Message`] to receive
125
+ /// * `patch_log` - A [`PatchLog`] which will be updated with any changes that are made to the current state of the document due to the received sync message
126
+ fn receive_sync_message_log_patches(
127
+ &mut self,
128
+ sync_state: &mut State,
129
+ message: Message,
130
+ patch_log: &mut PatchLog,
131
+ ) -> Result<(), AutomergeError>;
132
+ }
133
+
134
+ const MESSAGE_TYPE_SYNC: u8 = 0x42; // first byte of a sync message, for identification
135
+ const MESSAGE_TYPE_SYNC_V2: u8 = 0x43; // first byte of a sync message, for identification
136
+
137
+ #[derive(Clone, Debug, PartialEq)]
138
+ pub enum MessageVersion {
139
+ V1,
140
+ V2,
141
+ }
142
+
143
+ impl MessageVersion {
144
+ fn parse(input: parse::Input<'_>) -> parse::ParseResult<'_, Self, ReadMessageError> {
145
+ let (i, first_byte) = parse::take1(input)?;
146
+ match first_byte {
147
+ MESSAGE_TYPE_SYNC => Ok((i, Self::V1)),
148
+ MESSAGE_TYPE_SYNC_V2 => Ok((i, Self::V2)),
149
+ _ => Err(parse::ParseError::Error(ReadMessageError::WrongType {
150
+ expected_one_of: vec![MESSAGE_TYPE_SYNC, MESSAGE_TYPE_SYNC_V2],
151
+ found: first_byte,
152
+ })),
153
+ }
154
+ }
155
+
156
+ fn encode(&self) -> u8 {
157
+ match self {
158
+ Self::V1 => MESSAGE_TYPE_SYNC,
159
+ Self::V2 => MESSAGE_TYPE_SYNC_V2,
160
+ }
161
+ }
162
+ }
163
+
164
+ impl SyncDoc for Automerge {
165
+ fn generate_sync_message(&self, sync_state: &mut State) -> Option<Message> {
166
+ let our_heads = self.get_heads();
167
+
168
+ let our_need = if sync_state.read_only {
169
+ vec![]
170
+ } else {
171
+ self.get_missing_deps(sync_state.their_heads.as_ref().unwrap_or(&vec![]))
172
+ };
173
+
174
+ let their_heads_set = if let Some(ref heads) = sync_state.their_heads {
175
+ heads.iter().collect::<HashSet<_>>()
176
+ } else {
177
+ HashSet::new()
178
+ };
179
+ let our_have = if our_need.iter().all(|hash| their_heads_set.contains(hash)) {
180
+ vec![self.make_bloom_filter(sync_state.shared_heads.clone())]
181
+ } else {
182
+ Vec::new()
183
+ };
184
+
185
+ if let Some(ref their_have) = sync_state.their_have {
186
+ if let Some(first_have) = their_have.first().as_ref() {
187
+ if !first_have
188
+ .last_sync
189
+ .iter()
190
+ .all(|hash| self.has_change(hash))
191
+ {
192
+ return Some(Message::reset(our_heads));
193
+ }
194
+ }
195
+ }
196
+
197
+ let message_builder = if sync_state.is_peer_read_only() {
198
+ // The remote peer is read-only and will ignore incoming changes.
199
+ // Skip computing and sending changes to save bandwidth.
200
+ MessageBuilder::new(vec![], sync_state)
201
+ } else if let Some((their_have, their_need)) = sync_state.their() {
202
+ if sync_state.send_doc() {
203
+ let hashes = self.change_graph.get_hashes(&[]);
204
+ MessageBuilder::new_v2(self.save(), hashes)
205
+ } else {
206
+ let all_hashes = self
207
+ .get_hashes_to_send(their_have, their_need)
208
+ .expect("Should have only used hashes that are in the document");
209
+ // deduplicate the changes to send with those we have already sent and clone it now
210
+ let hashes: Vec<_> = all_hashes
211
+ .into_iter()
212
+ .filter(|hash| !sync_state.sent_hashes.contains(hash))
213
+ .collect();
214
+ if hashes.len() > self.change_graph.len() / 3 && sync_state.supports_v2_messages() {
215
+ // sending more than a 1/3 of the document? send everything
216
+ let all_hashes = self.change_graph.get_hashes(&[]);
217
+ MessageBuilder::new_v2(self.save(), all_hashes)
218
+ } else {
219
+ let changes = self.get_changes_by_hashes(hashes.iter().copied()).ok()?;
220
+ MessageBuilder::new(changes, sync_state)
221
+ }
222
+ }
223
+ } else {
224
+ MessageBuilder::new(vec![], sync_state)
225
+ };
226
+
227
+ let heads_unchanged = sync_state.last_sent_heads == our_heads;
228
+
229
+ let heads_equal = sync_state.their_heads.as_ref() == Some(&our_heads);
230
+
231
+ if heads_unchanged && sync_state.have_responded {
232
+ if (heads_equal || sync_state.read_only) && message_builder.is_empty() {
233
+ return None;
234
+ }
235
+ if sync_state.in_flight {
236
+ return None;
237
+ }
238
+ }
239
+
240
+ sync_state.have_responded = true;
241
+ sync_state.last_sent_heads.clone_from(&our_heads);
242
+ sync_state.sent_hashes.extend(message_builder.hashes());
243
+
244
+ let mut flags = MessageFlags::new();
245
+ flags.set(MessageFlags::SUPPORTS_SYNC_RESET);
246
+ if sync_state.read_only {
247
+ flags.set(MessageFlags::READ_ONLY);
248
+ }
249
+
250
+ // When switching from read-only to read-write, we need the remote to
251
+ // clear its sent_hashes so it resends changes we previously ignored.
252
+ // Peers that advertise Capability::SyncReset understand the SyncReset
253
+ // flag. Old peers don't, so we send empty heads to simulate losing all
254
+ // local state, which triggers the same sent_hashes clearing via the
255
+ // existing "peer lost all data" code path.
256
+ let heads_to_send = if sync_state.needs_reset {
257
+ sync_state.needs_reset = false;
258
+ if sync_state.peer_supports_sync_reset() {
259
+ flags.set(MessageFlags::SYNC_RESET);
260
+ our_heads.clone()
261
+ } else {
262
+ vec![]
263
+ }
264
+ } else {
265
+ our_heads.clone()
266
+ };
267
+
268
+ let sync_message = message_builder
269
+ .heads(heads_to_send)
270
+ .have(our_have)
271
+ .need(our_need)
272
+ .flags(Some(flags))
273
+ .build();
274
+
275
+ sync_state.in_flight = true;
276
+ Some(sync_message)
277
+ }
278
+
279
+ fn receive_sync_message(
280
+ &mut self,
281
+ sync_state: &mut State,
282
+ message: Message,
283
+ ) -> Result<(), AutomergeError> {
284
+ let mut patch_log = PatchLog::inactive();
285
+ self.receive_sync_message_inner(sync_state, message, &mut patch_log)
286
+ }
287
+
288
+ fn receive_sync_message_log_patches(
289
+ &mut self,
290
+ sync_state: &mut State,
291
+ message: Message,
292
+ patch_log: &mut PatchLog,
293
+ ) -> Result<(), AutomergeError> {
294
+ self.receive_sync_message_inner(sync_state, message, patch_log)
295
+ }
296
+ }
297
+
298
+ impl Automerge {
299
+ #[inline(never)]
300
+ fn make_bloom_filter(&self, last_sync: Vec<ChangeHash>) -> Have {
301
+ let hashes = self.change_graph.get_hashes(&last_sync);
302
+ Have {
303
+ last_sync,
304
+ bloom: BloomFilter::from_hashes(hashes.iter()),
305
+ }
306
+ }
307
+
308
+ #[inline(never)]
309
+ fn get_hashes_to_send(
310
+ &self,
311
+ have: &[Have],
312
+ need: &[ChangeHash],
313
+ ) -> Result<Vec<ChangeHash>, AutomergeError> {
314
+ if have.is_empty() {
315
+ Ok(need.to_vec())
316
+ } else {
317
+ let mut last_sync_hashes = HashSet::new();
318
+ let mut bloom_filters = Vec::with_capacity(have.len());
319
+
320
+ for h in have {
321
+ let Have { last_sync, bloom } = h;
322
+ last_sync_hashes.extend(last_sync);
323
+ bloom_filters.push(bloom);
324
+ }
325
+ let last_sync_hashes = last_sync_hashes.into_iter().copied().collect::<Vec<_>>();
326
+
327
+ let hashes = self.change_graph.get_hashes(&last_sync_hashes);
328
+
329
+ let mut change_hashes = HashSet::with_capacity(hashes.len());
330
+ let mut dependents: HashMap<ChangeHash, Vec<ChangeHash>> = HashMap::new();
331
+ let mut hashes_to_send = HashSet::new();
332
+
333
+ for hash in &*hashes {
334
+ change_hashes.insert(*hash);
335
+
336
+ for dep in self.change_graph.deps(hash) {
337
+ dependents.entry(dep).or_default().push(*hash);
338
+ }
339
+
340
+ if bloom_filters.iter().all(|bloom| !bloom.contains_hash(hash)) {
341
+ hashes_to_send.insert(*hash);
342
+ }
343
+ }
344
+
345
+ let mut stack = hashes_to_send.iter().copied().collect::<Vec<_>>();
346
+ while let Some(hash) = stack.pop() {
347
+ if let Some(deps) = dependents.get(&hash) {
348
+ for dep in deps {
349
+ if hashes_to_send.insert(*dep) {
350
+ stack.push(*dep);
351
+ }
352
+ }
353
+ }
354
+ }
355
+
356
+ let mut final_hashes = Vec::with_capacity(hashes_to_send.len() + need.len());
357
+ for hash in need {
358
+ if !hashes_to_send.contains(hash) {
359
+ final_hashes.push(*hash);
360
+ }
361
+ }
362
+
363
+ for hash in &*hashes {
364
+ if hashes_to_send.contains(hash) {
365
+ final_hashes.push(*hash);
366
+ }
367
+ }
368
+ Ok(final_hashes)
369
+ }
370
+ }
371
+
372
+ #[inline(never)]
373
+ pub(crate) fn receive_sync_message_inner(
374
+ &mut self,
375
+ sync_state: &mut State,
376
+ message: Message,
377
+ patch_log: &mut PatchLog,
378
+ ) -> Result<(), AutomergeError> {
379
+ sync_state.in_flight = false;
380
+ let before_heads = self.get_heads();
381
+
382
+ let Message {
383
+ heads: message_heads,
384
+ changes: message_changes,
385
+ need: message_need,
386
+ have: message_have,
387
+ flags: message_flags,
388
+ ..
389
+ } = message;
390
+
391
+ if let Some(flags) = message_flags {
392
+ // Any peer that sends the flags section supports V2 messages —
393
+ // the flags section was introduced alongside V2 support.
394
+ let mut caps = vec![Capability::MessageV2];
395
+ if flags.contains(MessageFlags::SUPPORTS_SYNC_RESET) {
396
+ caps.push(Capability::SyncReset);
397
+ }
398
+ sync_state.their_capabilities = Some(caps);
399
+
400
+ // Process transient per-message signals
401
+ if flags.contains(MessageFlags::SYNC_RESET) {
402
+ sync_state.sent_hashes.clear();
403
+ }
404
+ sync_state.peer_read_only = flags.contains(MessageFlags::READ_ONLY);
405
+ }
406
+
407
+ let changes_is_empty = message_changes.is_empty();
408
+ if !changes_is_empty && !sync_state.read_only {
409
+ self.load_incremental_log_patches(&message_changes.join(), patch_log)?;
410
+ sync_state.shared_heads = advance_heads(
411
+ &before_heads.iter().collect(),
412
+ &self.get_heads().into_iter().collect(),
413
+ &sync_state.shared_heads,
414
+ );
415
+ }
416
+
417
+ // trim down the sent hashes to those that we know they haven't seen
418
+ self.filter_changes(&message_heads, &mut sync_state.sent_hashes)?;
419
+
420
+ if changes_is_empty && message_heads == before_heads {
421
+ sync_state.last_sent_heads.clone_from(&message_heads);
422
+ }
423
+
424
+ let known_heads = message_heads
425
+ .iter()
426
+ .filter(|head| self.has_change(head))
427
+ .collect::<Vec<_>>();
428
+ if known_heads.len() == message_heads.len() {
429
+ sync_state.shared_heads.clone_from(&message_heads);
430
+ // If the remote peer has lost all its data, reset our state to perform a full resync
431
+ if message_heads.is_empty() {
432
+ sync_state.last_sent_heads = Default::default();
433
+ sync_state.sent_hashes = Default::default();
434
+ }
435
+ } else {
436
+ sync_state.shared_heads = sync_state
437
+ .shared_heads
438
+ .iter()
439
+ .chain(known_heads)
440
+ .copied()
441
+ .unique()
442
+ .sorted()
443
+ .collect::<Vec<_>>();
444
+ }
445
+
446
+ sync_state.their_have = Some(message_have);
447
+ sync_state.their_heads = Some(message_heads);
448
+ sync_state.their_need = Some(message_need);
449
+
450
+ Ok(())
451
+ }
452
+ }
453
+
454
+ #[derive(Debug, thiserror::Error)]
455
+ pub enum ReadMessageError {
456
+ #[error("expected {expected_one_of:?} but found {found}")]
457
+ WrongType { expected_one_of: Vec<u8>, found: u8 },
458
+ #[error("{0}")]
459
+ Parse(String),
460
+ #[error(transparent)]
461
+ ReadChangeOps(#[from] ReadChangeOpError),
462
+ #[error("not enough input")]
463
+ NotEnoughInput,
464
+ }
465
+
466
+ impl From<parse::leb128::Error> for ReadMessageError {
467
+ fn from(e: parse::leb128::Error) -> Self {
468
+ ReadMessageError::Parse(e.to_string())
469
+ }
470
+ }
471
+
472
+ impl From<bloom::ParseError> for ReadMessageError {
473
+ fn from(e: bloom::ParseError) -> Self {
474
+ ReadMessageError::Parse(e.to_string())
475
+ }
476
+ }
477
+
478
+ impl From<crate::storage::change::ParseError> for ReadMessageError {
479
+ fn from(e: crate::storage::change::ParseError) -> Self {
480
+ ReadMessageError::Parse(format!("error parsing changes: {}", e))
481
+ }
482
+ }
483
+
484
+ impl From<ReadMessageError> for parse::ParseError<ReadMessageError> {
485
+ fn from(e: ReadMessageError) -> Self {
486
+ parse::ParseError::Error(e)
487
+ }
488
+ }
489
+
490
+ impl From<parse::ParseError<ReadMessageError>> for ReadMessageError {
491
+ fn from(p: parse::ParseError<ReadMessageError>) -> Self {
492
+ match p {
493
+ parse::ParseError::Error(e) => e,
494
+ parse::ParseError::Incomplete(..) => Self::NotEnoughInput,
495
+ }
496
+ }
497
+ }
498
+
499
+ /// The sync message to be sent.
500
+ ///
501
+ /// ## Notes about encoding
502
+ ///
503
+ /// There are two versions of the sync message, V1 and V2. The V1 message is the original message
504
+ /// which automerge shipped with and V2 is an extension which allows for encoding the changes as
505
+ /// either a list of changes or as a compressed document format. This makes syncing up for the
506
+ /// first time faster.
507
+ ///
508
+ /// Encoding this in a backwards compatible way is a bit tricky. The wire format of the v1 message
509
+ /// didn't allow for any forwards compatible changes. In order to accomodate this the first message
510
+ /// a peer sends is a V1 message with a length-prefixed `Vec<MessageFlag>` appended to it. For old
511
+ /// implementations this appended data is just ignored but new implementations read it and store
512
+ /// the advertised capabilities on the sync state. This allows new implementations to discover if
513
+ /// the remote peer supports the V2 message format and if so send a V2 message. The flags also
514
+ /// carry transient per-message signals such as read-only mode and sync reset.
515
+ #[derive(Clone, Debug, PartialEq)]
516
+ pub struct Message {
517
+ /// The heads of the sender.
518
+ pub heads: Vec<ChangeHash>,
519
+ /// The hashes of any changes that are being explicitly requested from the recipient.
520
+ pub need: Vec<ChangeHash>,
521
+ /// A summary of the changes that the sender already has.
522
+ pub have: Vec<Have>,
523
+ /// The changes for the recipient to apply.
524
+ ///
525
+ /// This is a Vec of bytes which should be passed to `Automerge::load_incremental`. The reason
526
+ /// it is a `Vec<Vec<u8>>` and not a `Vec<u8>` is that the V1 message format is a sequence of
527
+ /// change chunks, each of which is length delimited. The V2 message format is a single length
528
+ /// delimited chunk but we nest it inside a Vec for backwards compatibility.
529
+ pub changes: ChunkList,
530
+ /// Per-message flags including capability advertisements and transient signals.
531
+ pub flags: Option<MessageFlags>,
532
+ /// What version to encode this message as
533
+ pub version: MessageVersion,
534
+ }
535
+
536
+ /// An array of changes, each of which should be passed to [`Automerge::load_incremental()`]
537
+ #[derive(Clone, Debug, PartialEq)]
538
+ pub struct ChunkList(Vec<Vec<u8>>);
539
+
540
+ impl From<Vec<Vec<u8>>> for ChunkList {
541
+ fn from(v: Vec<Vec<u8>>) -> Self {
542
+ Self(v)
543
+ }
544
+ }
545
+
546
+ impl From<Vec<u8>> for ChunkList {
547
+ fn from(v: Vec<u8>) -> Self {
548
+ Self(vec![v])
549
+ }
550
+ }
551
+
552
+ impl ChunkList {
553
+ fn parse(i: parse::Input<'_>) -> parse::ParseResult<'_, Self, ReadMessageError> {
554
+ let change_parser = |i| {
555
+ let (i, bytes) = parse::length_prefixed_bytes(i)?;
556
+ Ok((i, bytes.to_vec()))
557
+ };
558
+ let (i, stored_changes) = parse::length_prefixed(change_parser)(i)?;
559
+ Ok((i, Self(stored_changes)))
560
+ }
561
+
562
+ pub fn empty() -> Self {
563
+ Self(Vec::new())
564
+ }
565
+
566
+ pub fn is_empty(&self) -> bool {
567
+ self.0.is_empty()
568
+ }
569
+
570
+ pub fn len(&self) -> usize {
571
+ self.0.len()
572
+ }
573
+
574
+ pub fn iter(&self) -> impl ExactSizeIterator<Item = &[u8]> {
575
+ self.0.iter().map(|v| v.as_slice())
576
+ }
577
+
578
+ pub(crate) fn join(&self) -> Vec<u8> {
579
+ let total: usize = self.0.iter().map(Vec::len).sum();
580
+ let mut result = Vec::with_capacity(total);
581
+
582
+ for v in &self.0 {
583
+ result.extend(v);
584
+ }
585
+
586
+ result
587
+ }
588
+ }
589
+
590
+ impl serde::Serialize for Message {
591
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
592
+ where
593
+ S: serde::Serializer,
594
+ {
595
+ let mut map = serializer.serialize_map(Some(4))?;
596
+ map.serialize_entry("heads", &self.heads)?;
597
+ map.serialize_entry("need", &self.need)?;
598
+ map.serialize_entry("have", &self.have)?;
599
+ map.serialize_entry("changes", &self.changes.0)?;
600
+ map.end()
601
+ }
602
+ }
603
+
604
+ fn parse_have(input: parse::Input<'_>) -> parse::ParseResult<'_, Have, ReadMessageError> {
605
+ let (i, last_sync) = parse::length_prefixed(parse::change_hash)(input)?;
606
+ let (i, bloom_bytes) = parse::length_prefixed_bytes(i)?;
607
+ let (_, bloom) = BloomFilter::parse(parse::Input::new(bloom_bytes)).map_err(|e| e.lift())?;
608
+ Ok((i, Have { last_sync, bloom }))
609
+ }
610
+
611
+ impl Message {
612
+ pub(crate) fn reset(our_heads: Vec<ChangeHash>) -> Message {
613
+ Message {
614
+ heads: our_heads,
615
+ need: Vec::new(),
616
+ have: vec![Have::default()],
617
+ changes: ChunkList::empty(),
618
+ flags: {
619
+ let mut f = MessageFlags::new();
620
+ f.set(MessageFlags::SUPPORTS_SYNC_RESET);
621
+ Some(f)
622
+ },
623
+ version: MessageVersion::V1,
624
+ }
625
+ }
626
+
627
+ pub fn decode(input: &[u8]) -> Result<Self, ReadMessageError> {
628
+ let input = parse::Input::new(input);
629
+ match Self::parse(input) {
630
+ Ok((_, msg)) => Ok(msg),
631
+ Err(parse::ParseError::Error(e)) => Err(e),
632
+ Err(parse::ParseError::Incomplete(_)) => Err(ReadMessageError::NotEnoughInput),
633
+ }
634
+ }
635
+
636
+ pub(crate) fn parse(input: parse::Input<'_>) -> parse::ParseResult<'_, Self, ReadMessageError> {
637
+ let (i, message_version) = MessageVersion::parse(input)?;
638
+
639
+ let (i, heads) = parse::length_prefixed(parse::change_hash)(i)?;
640
+ let (i, need) = parse::length_prefixed(parse::change_hash)(i)?;
641
+ let (i, have) = parse::length_prefixed(parse_have)(i)?;
642
+
643
+ let (i, changes) = ChunkList::parse(i)?;
644
+ let (i, flags) = if !i.is_empty() {
645
+ let (i, raw_bytes) = parse::length_prefixed_bytes(i)?;
646
+ (i, Some(MessageFlags::parse_bytes(raw_bytes)))
647
+ } else {
648
+ (i, None)
649
+ };
650
+ Ok((
651
+ i,
652
+ Message {
653
+ heads,
654
+ need,
655
+ have,
656
+ changes,
657
+ flags,
658
+ version: message_version,
659
+ },
660
+ ))
661
+ }
662
+
663
+ pub fn encode(self) -> Vec<u8> {
664
+ let mut buf = vec![self.version.encode()];
665
+
666
+ encode_hashes(&mut buf, &self.heads);
667
+ encode_hashes(&mut buf, &self.need);
668
+ encode_many(&mut buf, self.have.iter(), |buf, h| {
669
+ encode_hashes(buf, &h.last_sync);
670
+ leb128::write::unsigned(buf, h.bloom.to_bytes().len() as u64).unwrap();
671
+ buf.extend(h.bloom.to_bytes());
672
+ });
673
+
674
+ encode_many(&mut buf, self.changes.iter(), |buf, change| {
675
+ leb128::write::unsigned(buf, change.len() as u64).unwrap();
676
+ buf.extend::<&[u8]>(change.as_ref())
677
+ });
678
+
679
+ if let Some(flags) = self.flags {
680
+ flags.encode(&mut buf);
681
+ }
682
+
683
+ buf
684
+ }
685
+ }
686
+
687
+ /// Per-message flags packed into a bitfield byte on the wire.
688
+ ///
689
+ /// ## Wire encoding
690
+ ///
691
+ /// Flags are encoded a little strangely, it's easiest to explain by explaining
692
+ /// the history. The first flag we added was to indicate that the other end
693
+ /// supported "v2" sync protocol messages - sync messages in which the entire
694
+ /// document state is sent in a single message. This flag was forward compatibly
695
+ /// encoded by taking advantage of the fact that the original parser would ignore
696
+ /// any bytes after the end of the sync message. We thus encoded new flags as
697
+ /// length prefixed set of flags at the end of the message. I.e. it looked
698
+ /// a bit like this:
699
+ ///
700
+ /// | message length | < sync message > | number of flags | flags |
701
+ ///
702
+ /// The idea was that this allows us to forward compatibly add new flags to the
703
+ /// protocol by just sending a longer list of flags. The problem with this
704
+ /// approach is that it usees a byte per flag, which becomes much more wasteful
705
+ /// as the number of flags grows. Thus, we now encode flags as bitfields. We
706
+ /// want to stay backwards compatible for implementations unaware of the bitfields
707
+ /// though. To do this we take advantage of the fact that there was only ever
708
+ /// one flag sent - the old `MessageV2` flag. This means that the only flags
709
+ /// ever sent were:
710
+ ///
711
+ /// | 0x01 | 0x02 |
712
+ ///
713
+ /// Where `0x01` is the length prefix byte and `0x02` is the old `MessageV2` flag.
714
+ /// To stay backwards compatible we encode the old `MessageV2` flag as a single byte
715
+ /// and then all the remaining flags as a byte with the high bit set. The old
716
+ /// code thus sees a bunch of unknown bytes after the old MessageV2 flag and
717
+ /// ignores them, the new code reads every bit and parses any byte with it's high
718
+ /// bit set as a bitfield of flags. This scheme allows us to forward compatibly
719
+ /// add as many flags as we need (by increasing the length prefix) without breaking
720
+ /// old implementations.
721
+ ///
722
+ /// The final encoding then is
723
+ ///
724
+ /// | length prefix | old MessageV2 flag | bitfield byte (0x80 set) |
725
+ ///
726
+ /// When parsing messages from old implementations (all bytes < 0x80), the
727
+ /// old individual byte values are mapped to the corresponding flag bits.
728
+ #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
729
+ pub struct MessageFlags(u8);
730
+
731
+ impl MessageFlags {
732
+ // V1/V2 message format support is not encoded in the bitfield — V2 is
733
+ // communicated via the legacy `0x02` byte that always precedes the
734
+ // bitfield, and V1 is the default. This leaves all 7 bitfield bits
735
+ // available for future flags.
736
+
737
+ /// Signals the remote peer should clear its `sent_hashes` and perform a
738
+ /// fresh sync. Used when switching from read-only to read-write mode.
739
+ pub const SYNC_RESET: u8 = 1 << 0;
740
+ /// Indicates the sender is in read-only mode and will not apply incoming
741
+ /// changes.
742
+ pub const READ_ONLY: u8 = 1 << 1;
743
+ /// Advertises that the sender understands the [`SYNC_RESET`](Self::SYNC_RESET)
744
+ /// flag and will clear `sent_hashes` when it receives one.
745
+ pub const SUPPORTS_SYNC_RESET: u8 = 1 << 2;
746
+
747
+ const BITFIELD_MARKER: u8 = 0x80;
748
+ /// The old MessageV2 byte, sent first for backwards compatibility.
749
+ const LEGACY_V2_BYTE: u8 = 0x02;
750
+
751
+ pub fn new() -> Self {
752
+ Self(0)
753
+ }
754
+
755
+ pub fn contains(self, flag: u8) -> bool {
756
+ self.0 & flag != 0
757
+ }
758
+
759
+ pub fn set(&mut self, flag: u8) {
760
+ self.0 |= flag;
761
+ }
762
+
763
+ fn encode(&self, out: &mut Vec<u8>) {
764
+ // Two entries: 0x02 (old MessageV2 for backwards compat) + bitfield byte
765
+ leb128::write::unsigned(out, 2u64).unwrap();
766
+ out.push(Self::LEGACY_V2_BYTE);
767
+ out.push(Self::BITFIELD_MARKER | self.0);
768
+ }
769
+
770
+ fn parse_bytes(bytes: &[u8]) -> Self {
771
+ let mut flags = Self::new();
772
+ for &byte in bytes {
773
+ if byte & Self::BITFIELD_MARKER != 0 {
774
+ // New-style bitfield byte: extract the flag bits
775
+ flags.0 |= byte & !Self::BITFIELD_MARKER;
776
+ }
777
+ // Old-style bytes (0x01, 0x02) are ignored — V1/V2 support
778
+ // is inferred from the presence of the flags section itself
779
+ }
780
+ flags
781
+ }
782
+ }
783
+
784
+ /// Persistent peer capabilities, derived from [`MessageFlags`]s on incoming
785
+ /// messages. These describe what the remote peer supports.
786
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
787
+ pub enum Capability {
788
+ MessageV1,
789
+ MessageV2,
790
+ /// The peer understands the [`MessageFlags::SYNC_RESET`] flag and will
791
+ /// clear its `sent_hashes` when it receives one.
792
+ SyncReset,
793
+ }
794
+
795
+ fn encode_many<'a, I, It, F>(out: &mut Vec<u8>, data: I, f: F)
796
+ where
797
+ I: Iterator<Item = It> + ExactSizeIterator + 'a,
798
+ F: Fn(&mut Vec<u8>, It),
799
+ {
800
+ leb128::write::unsigned(out, data.len() as u64).unwrap();
801
+ for datum in data {
802
+ f(out, datum)
803
+ }
804
+ }
805
+
806
+ fn encode_hashes(buf: &mut Vec<u8>, hashes: &[ChangeHash]) {
807
+ debug_assert!(
808
+ hashes.windows(2).all(|h| h[0] <= h[1]),
809
+ "hashes were not sorted"
810
+ );
811
+ encode_many(buf, hashes.iter(), |buf, hash| buf.extend(hash.as_bytes()))
812
+ }
813
+
814
+ fn advance_heads(
815
+ my_old_heads: &HashSet<&ChangeHash>,
816
+ my_new_heads: &HashSet<ChangeHash>,
817
+ our_old_shared_heads: &[ChangeHash],
818
+ ) -> Vec<ChangeHash> {
819
+ let new_heads = my_new_heads
820
+ .iter()
821
+ .filter(|head| !my_old_heads.contains(head))
822
+ .copied()
823
+ .collect::<Vec<_>>();
824
+
825
+ let common_heads = our_old_shared_heads
826
+ .iter()
827
+ .filter(|head| my_new_heads.contains(head))
828
+ .copied()
829
+ .collect::<Vec<_>>();
830
+
831
+ let mut advanced_heads = HashSet::with_capacity(new_heads.len() + common_heads.len());
832
+ for head in new_heads.into_iter().chain(common_heads) {
833
+ advanced_heads.insert(head);
834
+ }
835
+ let mut advanced_heads = advanced_heads.into_iter().collect::<Vec<_>>();
836
+ advanced_heads.sort();
837
+ advanced_heads
838
+ }
839
+
840
+ #[cfg(test)]
841
+ mod tests {
842
+ use super::*;
843
+ //use crate::change::gen::gen_change;
844
+ use crate::storage::parse::Input;
845
+ use crate::storage::Chunk;
846
+ use crate::transaction::Transactable;
847
+ //use crate::types::gen::gen_hash;
848
+ use crate::ActorId;
849
+ //use proptest::prelude::*;
850
+
851
+ /*
852
+ prop_compose! {
853
+ fn gen_bloom()(hashes in gen_sorted_hashes(0..10)) -> BloomFilter {
854
+ BloomFilter::from_hashes(hashes.into_iter())
855
+ }
856
+ }
857
+
858
+ prop_compose! {
859
+ fn gen_have()(bloom in gen_bloom(), last_sync in gen_sorted_hashes(0..10)) -> Have {
860
+ Have {
861
+ bloom,
862
+ last_sync,
863
+ }
864
+ }
865
+ }
866
+
867
+ fn gen_sorted_hashes(size: std::ops::Range<usize>) -> impl Strategy<Value = Vec<ChangeHash>> {
868
+ proptest::collection::vec(gen_hash(), size).prop_map(|mut h| {
869
+ h.sort();
870
+ h
871
+ })
872
+ }
873
+
874
+ prop_compose! {
875
+ fn gen_sync_message_v1()(
876
+ heads in gen_sorted_hashes(0..10),
877
+ need in gen_sorted_hashes(0..10),
878
+ have in proptest::collection::vec(gen_have(), 0..10),
879
+ changes in proptest::collection::vec(gen_change(), 0..10),
880
+ supported_capabilities in prop_oneof![
881
+ Just(None),
882
+ Just(Some(vec![Capability::MessageV1])),
883
+ Just(Some(vec![Capability::MessageV2])),
884
+ Just(Some(vec![Capability::MessageV1, Capability::MessageV2])),
885
+ ],
886
+ ) -> Message {
887
+ Message {
888
+ heads,
889
+ need,
890
+ have,
891
+ changes: changes.into_iter().map(|c| c.raw_bytes().to_vec()).collect::<Vec<Vec<u8>>>().into(),
892
+ supported_capabilities,
893
+ version: MessageVersion::V1,
894
+ }
895
+ }
896
+ }
897
+ */
898
+
899
+ /*
900
+ prop_compose! {
901
+ fn gen_sync_message_v2()(
902
+ heads in gen_sorted_hashes(0..10),
903
+ need in gen_sorted_hashes(0..10),
904
+ have in proptest::collection::vec(gen_have(), 0..10),
905
+ raw in proptest::collection::vec(any::<u8>(), 0..100),
906
+ supported_capabilities in prop_oneof![
907
+ Just(None),
908
+ Just(Some(vec![Capability::MessageV1])),
909
+ Just(Some(vec![Capability::MessageV2])),
910
+ Just(Some(vec![Capability::MessageV1, Capability::MessageV2])),
911
+ ],
912
+ ) -> Message {
913
+ Message {
914
+ heads,
915
+ need,
916
+ have,
917
+ changes: ChunkList::from(raw),
918
+ supported_capabilities,
919
+ version: MessageVersion::V2,
920
+ }
921
+ }
922
+ }
923
+ */
924
+
925
+ /*
926
+ fn gen_sync_message() -> impl Strategy<Value = Message> {
927
+ prop_oneof![gen_sync_message_v1(), gen_sync_message_v2(),].boxed()
928
+ }
929
+ */
930
+
931
+ #[test]
932
+ fn encode_decode_empty_message() {
933
+ let msg = Message {
934
+ heads: vec![],
935
+ need: vec![],
936
+ have: vec![],
937
+ changes: ChunkList::empty(),
938
+ flags: None,
939
+ version: MessageVersion::V2,
940
+ };
941
+ let encoded = msg.encode();
942
+ Message::parse(Input::new(&encoded)).unwrap();
943
+ }
944
+
945
+ /*
946
+ proptest! {
947
+ #[test]
948
+ fn encode_decode_message(msg in gen_sync_message()) {
949
+ let encoded = msg.clone().encode();
950
+ let (i, decoded) = Message::parse(Input::new(&encoded)).unwrap();
951
+ assert!(i.is_empty());
952
+ assert_eq!(msg, decoded);
953
+ }
954
+ }
955
+ */
956
+
957
+ #[test]
958
+ fn generate_sync_message_twice_does_nothing() {
959
+ let mut doc = crate::AutoCommit::new();
960
+ doc.put(crate::ROOT, "key", "value").unwrap();
961
+ let mut sync_state = State::new();
962
+
963
+ assert!(doc.sync().generate_sync_message(&mut sync_state).is_some());
964
+ assert!(doc.sync().generate_sync_message(&mut sync_state).is_none());
965
+ }
966
+
967
+ #[test]
968
+ fn first_response_is_some_even_if_no_changes() {
969
+ // The first time we generate a sync message for a given peer we should always send a
970
+ // response so that they know what our heads are, even if we are at the same heads as them
971
+
972
+ let mut doc1 = crate::AutoCommit::new();
973
+ doc1.put(crate::ROOT, "key", "value").unwrap();
974
+ let mut doc2 = doc1.fork();
975
+
976
+ let mut s1 = State::new();
977
+ let mut s2 = State::new();
978
+
979
+ let m1 = doc1
980
+ .sync()
981
+ .generate_sync_message(&mut s1)
982
+ .expect("message was none");
983
+
984
+ doc2.sync().receive_sync_message(&mut s2, m1).unwrap();
985
+
986
+ let _m2 = doc2
987
+ .sync()
988
+ .generate_sync_message(&mut s2)
989
+ .expect("response was none");
990
+ }
991
+
992
+ #[test]
993
+ fn should_not_reply_if_we_have_no_data_after_first_round() {
994
+ let mut doc1 = crate::AutoCommit::new();
995
+ let mut doc2 = crate::AutoCommit::new();
996
+ let mut s1 = State::new();
997
+ let mut s2 = State::new();
998
+ let m1 = doc1
999
+ .sync()
1000
+ .generate_sync_message(&mut s1)
1001
+ .expect("message was none");
1002
+
1003
+ doc2.sync().receive_sync_message(&mut s2, m1).unwrap();
1004
+ let _m2 = doc2
1005
+ .sync()
1006
+ .generate_sync_message(&mut s2)
1007
+ .expect("first round message was none");
1008
+
1009
+ let m1 = doc1.sync().generate_sync_message(&mut s1);
1010
+ assert!(m1.is_none());
1011
+
1012
+ let m2 = doc2.sync().generate_sync_message(&mut s2);
1013
+ assert!(m2.is_none());
1014
+ }
1015
+
1016
+ #[test]
1017
+ fn should_allow_simultaneous_messages_during_synchronisation() {
1018
+ // create & synchronize two nodes
1019
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1020
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1021
+ let mut s1 = State::new();
1022
+ let mut s2 = State::new();
1023
+
1024
+ for i in 0..5 {
1025
+ doc1.put(&crate::ROOT, "x", i).unwrap();
1026
+ doc1.commit();
1027
+ doc2.put(&crate::ROOT, "y", i).unwrap();
1028
+ doc2.commit();
1029
+ }
1030
+
1031
+ let head1 = doc1.get_heads()[0];
1032
+ let head2 = doc2.get_heads()[0];
1033
+
1034
+ //// both sides report what they have but have no shared peer state
1035
+ let msg1to2 = doc1
1036
+ .sync()
1037
+ .generate_sync_message(&mut s1)
1038
+ .expect("initial sync from 1 to 2 was None");
1039
+ let msg2to1 = doc2
1040
+ .sync()
1041
+ .generate_sync_message(&mut s2)
1042
+ .expect("initial sync message from 2 to 1 was None");
1043
+ let Message {
1044
+ changes: changes1to2,
1045
+ ..
1046
+ } = &msg1to2;
1047
+ assert_eq!(changes1to2.len(), 0);
1048
+ assert_eq!(msg1to2.have[0].last_sync.len(), 0);
1049
+ let Message {
1050
+ changes: changes2to1,
1051
+ ..
1052
+ } = &msg2to1;
1053
+ assert_eq!(changes2to1.len(), 0);
1054
+ assert_eq!(msg2to1.have[0].last_sync.len(), 0);
1055
+
1056
+ //// doc1 and doc2 receive that message and update sync state
1057
+ doc1.sync().receive_sync_message(&mut s1, msg2to1).unwrap();
1058
+ doc2.sync().receive_sync_message(&mut s2, msg1to2).unwrap();
1059
+
1060
+ //// now both reply with their local changes the other lacks
1061
+ //// (standard warning that 1% of the time this will result in a "need" message)
1062
+ let msg1to2 = doc1
1063
+ .sync()
1064
+ .generate_sync_message(&mut s1)
1065
+ .expect("first reply from 1 to 2 was None");
1066
+ let Message {
1067
+ changes: changes1to2,
1068
+ ..
1069
+ } = &msg1to2;
1070
+ assert!(!changes1to2.is_empty());
1071
+
1072
+ let msg2to1 = doc2
1073
+ .sync()
1074
+ .generate_sync_message(&mut s2)
1075
+ .expect("first reply from 2 to 1 was None");
1076
+ let Message {
1077
+ changes: changes2to1,
1078
+ ..
1079
+ } = &msg2to1;
1080
+ assert!(!changes2to1.is_empty());
1081
+
1082
+ //// both should now apply the changes
1083
+ doc1.sync().receive_sync_message(&mut s1, msg2to1).unwrap();
1084
+ assert_eq!(doc1.get_missing_deps(&[]), Vec::new());
1085
+
1086
+ doc2.sync().receive_sync_message(&mut s2, msg1to2).unwrap();
1087
+ assert_eq!(doc2.get_missing_deps(&[]), Vec::new());
1088
+
1089
+ //// The response acknowledges the changes received and sends no further changes
1090
+ let msg1to2 = doc1
1091
+ .sync()
1092
+ .generate_sync_message(&mut s1)
1093
+ .expect("second reply from 1 to 2 was None");
1094
+ let Message {
1095
+ changes: changes1to2,
1096
+ ..
1097
+ } = &msg1to2;
1098
+ assert_eq!(changes1to2.len(), 0);
1099
+ let msg2to1 = doc2
1100
+ .sync()
1101
+ .generate_sync_message(&mut s2)
1102
+ .expect("second reply from 2 to 1 was None");
1103
+ let Message {
1104
+ changes: changes2to1,
1105
+ ..
1106
+ } = &msg2to1;
1107
+ assert_eq!(changes2to1.len(), 0);
1108
+
1109
+ //// After receiving acknowledgements, their shared heads should be equal
1110
+ doc1.sync().receive_sync_message(&mut s1, msg2to1).unwrap();
1111
+ doc2.sync().receive_sync_message(&mut s2, msg1to2).unwrap();
1112
+
1113
+ assert_eq!(s1.shared_heads, s2.shared_heads);
1114
+
1115
+ //// We're in sync, no more messages required
1116
+ assert!(doc1.sync().generate_sync_message(&mut s1).is_none());
1117
+ assert!(doc2.sync().generate_sync_message(&mut s2).is_none());
1118
+
1119
+ //// If we make one more change and start another sync then its lastSync should be updated
1120
+ doc1.put(crate::ROOT, "x", 5).unwrap();
1121
+ doc1.commit();
1122
+ let msg1to2 = doc1
1123
+ .sync()
1124
+ .generate_sync_message(&mut s1)
1125
+ .expect("third reply from 1 to 2 was None");
1126
+ let mut expected_heads = vec![head1, head2];
1127
+ expected_heads.sort();
1128
+ let mut actual_heads = msg1to2.have[0].last_sync.clone();
1129
+ actual_heads.sort();
1130
+ assert_eq!(actual_heads, expected_heads);
1131
+ }
1132
+
1133
+ #[test]
1134
+ fn should_handle_false_positive_head() {
1135
+ // Scenario: ,-- n1
1136
+ // c0 <-- c1 <-- c2 <-- c3 <-- c4 <-- c5 <-- c6 <-- c7 <-- c8 <-- c9 <-+
1137
+ // `-- n2
1138
+ // where n2 is a false positive in the Bloom filter containing {n1}.
1139
+ // lastSync is c9.
1140
+
1141
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1142
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1143
+ let mut s1 = State::new();
1144
+ let mut s2 = State::new();
1145
+
1146
+ for i in 0..10 {
1147
+ doc1.put(crate::ROOT, "x", i).unwrap();
1148
+ doc1.commit();
1149
+ }
1150
+
1151
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1152
+
1153
+ // search for false positive; see comment above
1154
+ let mut i = 0;
1155
+ let (mut doc1, mut doc2) = loop {
1156
+ let mut doc1copy = doc1
1157
+ .clone()
1158
+ .with_actor(ActorId::try_from("01234567").unwrap());
1159
+ let val1 = format!("{} @ n1", i);
1160
+ doc1copy.put(crate::ROOT, "x", val1).unwrap();
1161
+ doc1copy.commit();
1162
+
1163
+ let mut doc2copy = doc1
1164
+ .clone()
1165
+ .with_actor(ActorId::try_from("89abcdef").unwrap());
1166
+ let val2 = format!("{} @ n2", i);
1167
+ doc2copy.put(crate::ROOT, "x", val2).unwrap();
1168
+ doc2copy.commit();
1169
+
1170
+ let n1_bloom = BloomFilter::from_hashes(doc1copy.get_heads().into_iter());
1171
+ if n1_bloom.contains_hash(&doc2copy.get_heads()[0]) {
1172
+ break (doc1copy, doc2copy);
1173
+ }
1174
+ i += 1;
1175
+ };
1176
+
1177
+ let mut all_heads = doc1.get_heads();
1178
+ all_heads.extend(doc2.get_heads());
1179
+ all_heads.sort();
1180
+
1181
+ // reset sync states
1182
+ let (_, mut s1) = State::parse(Input::new(s1.encode().as_slice())).unwrap();
1183
+ let (_, mut s2) = State::parse(Input::new(s2.encode().as_slice())).unwrap();
1184
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1185
+ assert_eq!(doc1.get_heads(), all_heads);
1186
+ assert_eq!(doc2.get_heads(), all_heads);
1187
+ }
1188
+
1189
+ #[test]
1190
+ fn should_handle_chains_of_false_positives() {
1191
+ //// Scenario: ,-- c5
1192
+ //// c0 <-- c1 <-- c2 <-- c3 <-- c4 <-+
1193
+ //// `-- n2c1 <-- n2c2 <-- n2c3
1194
+ //// where n2c1 and n2c2 are both false positives in the Bloom filter containing {c5}.
1195
+ //// lastSync is c4.
1196
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1197
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1198
+ let mut s1 = State::new();
1199
+ let mut s2 = State::new();
1200
+
1201
+ for i in 0..10 {
1202
+ doc1.put(crate::ROOT, "x", i).unwrap();
1203
+ doc1.commit();
1204
+ }
1205
+
1206
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1207
+
1208
+ doc1.put(crate::ROOT, "x", 5).unwrap();
1209
+ doc1.commit();
1210
+ let bloom = BloomFilter::from_hashes(doc1.get_heads().into_iter());
1211
+
1212
+ // search for false positive; see comment above
1213
+ let mut i = 0;
1214
+ let mut doc2 = loop {
1215
+ let mut doc = doc2
1216
+ .fork()
1217
+ .with_actor(ActorId::try_from("89abcdef").unwrap());
1218
+ doc.put(crate::ROOT, "x", format!("{} at 89abdef", i))
1219
+ .unwrap();
1220
+ doc.commit();
1221
+ if bloom.contains_hash(&doc.get_heads()[0]) {
1222
+ break doc;
1223
+ }
1224
+ i += 1;
1225
+ };
1226
+
1227
+ // find another false positive building on the first
1228
+ i = 0;
1229
+ let mut doc2 = loop {
1230
+ let mut doc = doc2
1231
+ .fork()
1232
+ .with_actor(ActorId::try_from("89abcdef").unwrap());
1233
+ doc.put(crate::ROOT, "x", format!("{} again", i)).unwrap();
1234
+ doc.commit();
1235
+ if bloom.contains_hash(&doc.get_heads()[0]) {
1236
+ break doc;
1237
+ }
1238
+ i += 1;
1239
+ };
1240
+
1241
+ doc2.put(crate::ROOT, "x", "final @ 89abcdef").unwrap();
1242
+
1243
+ let mut all_heads = doc1.get_heads();
1244
+ all_heads.extend(doc2.get_heads());
1245
+ all_heads.sort();
1246
+
1247
+ let (_, mut s1) = State::parse(Input::new(s1.encode().as_slice())).unwrap();
1248
+ let (_, mut s2) = State::parse(Input::new(s2.encode().as_slice())).unwrap();
1249
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1250
+ assert_eq!(doc1.get_heads(), all_heads);
1251
+ assert_eq!(doc2.get_heads(), all_heads);
1252
+ }
1253
+
1254
+ #[test]
1255
+ fn should_handle_lots_of_branching_and_merging() {
1256
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("01234567").unwrap());
1257
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("89abcdef").unwrap());
1258
+ let mut doc3 = crate::AutoCommit::new().with_actor(ActorId::try_from("fedcba98").unwrap());
1259
+ let mut s1 = State::new();
1260
+ let mut s2 = State::new();
1261
+
1262
+ doc1.put(crate::ROOT, "x", 0).unwrap();
1263
+ let change1 = doc1.get_last_local_change().unwrap().clone();
1264
+
1265
+ doc2.apply_changes([change1.clone()]).unwrap();
1266
+ doc3.apply_changes([change1]).unwrap();
1267
+
1268
+ doc3.put(crate::ROOT, "x", 1).unwrap();
1269
+
1270
+ //// - n1c1 <------ n1c2 <------ n1c3 <-- etc. <-- n1c20 <------ n1c21
1271
+ //// / \/ \/ \/
1272
+ //// / /\ /\ /\
1273
+ //// c0 <---- n2c1 <------ n2c2 <------ n2c3 <-- etc. <-- n2c20 <------ n2c21
1274
+ //// \ /
1275
+ //// ---------------------------------------------- n3c1 <-----
1276
+ for i in 1..20 {
1277
+ doc1.put(crate::ROOT, "n1", i).unwrap();
1278
+ doc2.put(crate::ROOT, "n2", i).unwrap();
1279
+ let change1 = doc1.get_last_local_change().unwrap().clone();
1280
+ let change2 = doc2.get_last_local_change().unwrap().clone();
1281
+ doc1.apply_changes([change2.clone()]).unwrap();
1282
+ doc2.apply_changes([change1]).unwrap();
1283
+ }
1284
+
1285
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1286
+
1287
+ //// Having n3's last change concurrent to the last sync heads forces us into the slower code path
1288
+ let change3 = doc3.get_last_local_change().unwrap().clone();
1289
+ doc2.apply_changes([change3]).unwrap();
1290
+
1291
+ doc1.put(crate::ROOT, "n1", "final").unwrap();
1292
+ doc2.put(crate::ROOT, "n1", "final").unwrap();
1293
+
1294
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1295
+
1296
+ assert_eq!(doc1.get_heads(), doc2.get_heads());
1297
+ }
1298
+
1299
+ #[test]
1300
+ fn in_flight_logic_should_not_sabotage_concurrent_changes() {
1301
+ // This reproduces issue https://github.com/automerge/automerge/issues/702
1302
+ //
1303
+ // This problem manifested as a situation where the sync states of two
1304
+ // ends of a connection both return None from `generate_sync_message` -
1305
+ // indicating that there is nothing to send - yet the documents were
1306
+ // different at either end.
1307
+
1308
+ // Because this logic depends on bloom filter false positives we have to
1309
+ // run the test many times, hence this loop
1310
+ for _ in 0..300 {
1311
+ // create two documents
1312
+ let mut doc1 = crate::AutoCommit::new();
1313
+ let mut doc2 = crate::AutoCommit::new();
1314
+ let mut s1 = State::new();
1315
+ let mut s2 = State::new();
1316
+
1317
+ // get them in sync
1318
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1319
+
1320
+ // make a change on doc2
1321
+ doc2.put(crate::ROOT, "x", 0).unwrap();
1322
+
1323
+ // generate a sync message containing the change (this should
1324
+ // alwasy be Some because we have generated new local changes)
1325
+ let msg = doc2.sync().generate_sync_message(&mut s2).unwrap();
1326
+ // Receive that sync message on doc1
1327
+ doc1.sync().receive_sync_message(&mut s1, msg).unwrap();
1328
+
1329
+ // now before sending any messages back to doc2, make a change on
1330
+ // doc1
1331
+ doc1.put(crate::ROOT, "x", 1).unwrap();
1332
+
1333
+ // now synchronize
1334
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1335
+
1336
+ // At this point both documents should be equal
1337
+ assert_eq!(doc1.get_heads(), doc2.get_heads());
1338
+ }
1339
+ }
1340
+
1341
+ fn sync(
1342
+ a: &mut crate::AutoCommit,
1343
+ b: &mut crate::AutoCommit,
1344
+ a_sync_state: &mut State,
1345
+ b_sync_state: &mut State,
1346
+ ) {
1347
+ //function sync(a: Automerge, b: Automerge, aSyncState = initSyncState(), bSyncState = initSyncState()) {
1348
+ const MAX_ITER: usize = 10;
1349
+ let mut iterations = 0;
1350
+
1351
+ loop {
1352
+ let a_to_b = a.sync().generate_sync_message(a_sync_state);
1353
+ let b_to_a = b.sync().generate_sync_message(b_sync_state);
1354
+ if a_to_b.is_none() && b_to_a.is_none() {
1355
+ break;
1356
+ }
1357
+ if iterations > MAX_ITER {
1358
+ panic!("failed to sync in {} iterations", MAX_ITER);
1359
+ }
1360
+ if let Some(msg) = a_to_b {
1361
+ b.sync().receive_sync_message(b_sync_state, msg).unwrap()
1362
+ }
1363
+ if let Some(msg) = b_to_a {
1364
+ a.sync().receive_sync_message(a_sync_state, msg).unwrap()
1365
+ }
1366
+ iterations += 1;
1367
+ }
1368
+ }
1369
+
1370
+ #[test]
1371
+ fn if_first_message_has_no_heads_and_supports_v2_message_send_whole_doc() {
1372
+ let mut doc1 = crate::AutoCommit::new();
1373
+ let mut doc2 = crate::AutoCommit::new();
1374
+ doc2.put(crate::ROOT, "foo", "bar").unwrap();
1375
+
1376
+ let mut s1 = State::new();
1377
+ let mut s2 = State::new();
1378
+
1379
+ let outgoing = doc1
1380
+ .sync()
1381
+ .generate_sync_message(&mut s1)
1382
+ .expect("message was none");
1383
+
1384
+ doc2.sync().receive_sync_message(&mut s2, outgoing).unwrap();
1385
+
1386
+ let response = doc2
1387
+ .sync()
1388
+ .generate_sync_message(&mut s2)
1389
+ .expect("response was none");
1390
+
1391
+ let Message { changes, .. } = response;
1392
+
1393
+ let (_, chunk) = Chunk::parse(Input::new(&changes.0[0])).unwrap();
1394
+ assert!(matches!(chunk, Chunk::Document(_)));
1395
+ }
1396
+
1397
+ #[test]
1398
+ fn read_only_sync_does_not_apply_incoming_changes() {
1399
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1400
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1401
+
1402
+ doc1.put(crate::ROOT, "from_doc1", "hello").unwrap();
1403
+ doc1.commit();
1404
+ doc2.put(crate::ROOT, "from_doc2", "world").unwrap();
1405
+ doc2.commit();
1406
+
1407
+ let doc1_heads_before = doc1.get_heads();
1408
+ let doc2_heads_before = doc2.get_heads();
1409
+
1410
+ // doc1 is read-only: it should send its changes but not accept doc2's
1411
+ let mut s1 = State::new_read_only();
1412
+ let mut s2 = State::new();
1413
+
1414
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1415
+
1416
+ // doc2 should have received doc1's changes
1417
+ assert!(doc2.get(crate::ROOT, "from_doc1").unwrap().is_some());
1418
+ assert!(doc2.get(crate::ROOT, "from_doc2").unwrap().is_some());
1419
+
1420
+ // doc1 should NOT have received doc2's changes
1421
+ assert!(doc1.get(crate::ROOT, "from_doc1").unwrap().is_some());
1422
+ assert!(doc1.get(crate::ROOT, "from_doc2").unwrap().is_none());
1423
+
1424
+ // doc1's heads should be unchanged
1425
+ assert_eq!(doc1.get_heads(), doc1_heads_before);
1426
+
1427
+ // doc2's heads should have advanced
1428
+ assert_ne!(doc2.get_heads(), doc2_heads_before);
1429
+ }
1430
+
1431
+ #[test]
1432
+ fn read_only_empty_peer_syncs_with_data_peer() {
1433
+ // An empty read-only peer syncs with a peer that has data.
1434
+ // This exercises the V2 "send full document" path on the non-read-only side.
1435
+ // The read-only peer should ignore the document and the protocol should converge.
1436
+ let mut doc1 = crate::AutoCommit::new();
1437
+ let mut doc2 = crate::AutoCommit::new();
1438
+ doc2.put(crate::ROOT, "key", "value").unwrap();
1439
+ doc2.commit();
1440
+
1441
+ let mut s1 = State::new_read_only();
1442
+ let mut s2 = State::new();
1443
+
1444
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1445
+
1446
+ // doc1 should still be empty
1447
+ assert!(doc1.get(crate::ROOT, "key").unwrap().is_none());
1448
+ assert!(doc1.get_heads().is_empty());
1449
+
1450
+ // doc2 should be unchanged
1451
+ assert!(doc2.get(crate::ROOT, "key").unwrap().is_some());
1452
+ }
1453
+
1454
+ #[test]
1455
+ fn both_peers_read_only() {
1456
+ // When both peers are read-only, neither applies the other's changes.
1457
+ // The protocol should still converge.
1458
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1459
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1460
+
1461
+ doc1.put(crate::ROOT, "from_doc1", "hello").unwrap();
1462
+ doc1.commit();
1463
+ doc2.put(crate::ROOT, "from_doc2", "world").unwrap();
1464
+ doc2.commit();
1465
+
1466
+ let doc1_heads = doc1.get_heads();
1467
+ let doc2_heads = doc2.get_heads();
1468
+
1469
+ let mut s1 = State::new_read_only();
1470
+ let mut s2 = State::new_read_only();
1471
+
1472
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1473
+
1474
+ // Neither peer should have the other's changes
1475
+ assert!(doc1.get(crate::ROOT, "from_doc2").unwrap().is_none());
1476
+ assert!(doc2.get(crate::ROOT, "from_doc1").unwrap().is_none());
1477
+
1478
+ // Both heads unchanged
1479
+ assert_eq!(doc1.get_heads(), doc1_heads);
1480
+ assert_eq!(doc2.get_heads(), doc2_heads);
1481
+ }
1482
+
1483
+ #[test]
1484
+ fn both_peers_read_only_converges_to_none() {
1485
+ // Explicitly verify that generate_sync_message returns None on both
1486
+ // sides after the initial exchange between two read-only peers.
1487
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1488
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1489
+
1490
+ doc1.put(crate::ROOT, "from_doc1", "hello").unwrap();
1491
+ doc1.commit();
1492
+ doc2.put(crate::ROOT, "from_doc2", "world").unwrap();
1493
+ doc2.commit();
1494
+
1495
+ let mut s1 = State::new_read_only();
1496
+ let mut s2 = State::new_read_only();
1497
+
1498
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1499
+
1500
+ // After sync, both must return None — no infinite loop
1501
+ assert!(doc1.sync().generate_sync_message(&mut s1).is_none());
1502
+ assert!(doc2.sync().generate_sync_message(&mut s2).is_none());
1503
+
1504
+ // Both discover the other is read-only
1505
+ assert!(s1.is_peer_read_only());
1506
+ assert!(s2.is_peer_read_only());
1507
+ }
1508
+
1509
+ #[test]
1510
+ fn both_read_only_one_makes_local_changes() {
1511
+ // Both peers are read-only. One makes local changes between sync
1512
+ // rounds. The updated heads should be communicated (no changes sent),
1513
+ // and the protocol should converge.
1514
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1515
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1516
+
1517
+ let mut s1 = State::new_read_only();
1518
+ let mut s2 = State::new_read_only();
1519
+
1520
+ // Initial sync with no data — just exchange hellos
1521
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1522
+
1523
+ // doc1 makes local changes
1524
+ doc1.put(crate::ROOT, "key", "value1").unwrap();
1525
+ doc1.commit();
1526
+ let doc1_heads_after = doc1.get_heads();
1527
+
1528
+ // Sync again — should converge, doc1's new heads communicated
1529
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1530
+
1531
+ // doc2 should know about doc1's heads but not have the changes
1532
+ assert_eq!(s2.their_heads.as_ref().unwrap(), &doc1_heads_after);
1533
+ assert!(doc2.get(crate::ROOT, "key").unwrap().is_none());
1534
+
1535
+ // doc1 makes more changes
1536
+ doc1.put(crate::ROOT, "key", "value2").unwrap();
1537
+ doc1.commit();
1538
+ let doc1_heads_after2 = doc1.get_heads();
1539
+
1540
+ // Sync again
1541
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1542
+
1543
+ // doc2 should see the updated heads
1544
+ assert_eq!(s2.their_heads.as_ref().unwrap(), &doc1_heads_after2);
1545
+ assert!(doc2.get(crate::ROOT, "key").unwrap().is_none());
1546
+
1547
+ // Must converge
1548
+ assert!(doc1.sync().generate_sync_message(&mut s1).is_none());
1549
+ assert!(doc2.sync().generate_sync_message(&mut s2).is_none());
1550
+ }
1551
+
1552
+ #[test]
1553
+ fn both_read_only_both_make_local_changes() {
1554
+ // Both peers are read-only and both make local changes between sync
1555
+ // rounds. Both should learn the other's updated heads, neither should
1556
+ // receive actual changes, and the protocol should converge each round.
1557
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1558
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1559
+
1560
+ let mut s1 = State::new_read_only();
1561
+ let mut s2 = State::new_read_only();
1562
+
1563
+ for round in 0..5 {
1564
+ doc1.put(crate::ROOT, "doc1_counter", round as i64).unwrap();
1565
+ doc1.commit();
1566
+ doc2.put(crate::ROOT, "doc2_counter", round as i64).unwrap();
1567
+ doc2.commit();
1568
+
1569
+ let doc1_heads = doc1.get_heads();
1570
+ let doc2_heads = doc2.get_heads();
1571
+
1572
+ // Must converge each round (sync helper panics after 10 iterations)
1573
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1574
+
1575
+ // Each peer should know the other's heads
1576
+ assert_eq!(
1577
+ s1.their_heads.as_ref().unwrap(),
1578
+ &doc2_heads,
1579
+ "round {round}: doc1 should know doc2's heads"
1580
+ );
1581
+ assert_eq!(
1582
+ s2.their_heads.as_ref().unwrap(),
1583
+ &doc1_heads,
1584
+ "round {round}: doc2 should know doc1's heads"
1585
+ );
1586
+
1587
+ // Neither should have the other's data
1588
+ assert!(
1589
+ doc1.get(crate::ROOT, "doc2_counter").unwrap().is_none(),
1590
+ "round {round}: doc1 should not have doc2's changes"
1591
+ );
1592
+ assert!(
1593
+ doc2.get(crate::ROOT, "doc1_counter").unwrap().is_none(),
1594
+ "round {round}: doc2 should not have doc1's changes"
1595
+ );
1596
+
1597
+ // Must be fully converged
1598
+ assert!(
1599
+ doc1.sync().generate_sync_message(&mut s1).is_none(),
1600
+ "round {round}: doc1 should have nothing more to send"
1601
+ );
1602
+ assert!(
1603
+ doc2.sync().generate_sync_message(&mut s2).is_none(),
1604
+ "round {round}: doc2 should have nothing more to send"
1605
+ );
1606
+ }
1607
+ }
1608
+
1609
+ #[test]
1610
+ fn both_read_only_simultaneous_changes_during_sync() {
1611
+ // Both peers are read-only. Both make changes, then exchange messages
1612
+ // simultaneously (like the simultaneous sync test). Must converge.
1613
+ for _ in 0..100 {
1614
+ let mut doc1 =
1615
+ crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1616
+ let mut doc2 =
1617
+ crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1618
+
1619
+ let mut s1 = State::new_read_only();
1620
+ let mut s2 = State::new_read_only();
1621
+
1622
+ // Both make changes
1623
+ doc1.put(crate::ROOT, "x", 1).unwrap();
1624
+ doc1.commit();
1625
+ doc2.put(crate::ROOT, "y", 2).unwrap();
1626
+ doc2.commit();
1627
+
1628
+ // Sync (uses the helper which panics on non-convergence)
1629
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1630
+
1631
+ // Now both make MORE changes
1632
+ doc1.put(crate::ROOT, "x", 3).unwrap();
1633
+ doc1.commit();
1634
+ doc2.put(crate::ROOT, "y", 4).unwrap();
1635
+ doc2.commit();
1636
+
1637
+ // Sync again
1638
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1639
+
1640
+ // Must be fully converged
1641
+ assert!(doc1.sync().generate_sync_message(&mut s1).is_none());
1642
+ assert!(doc2.sync().generate_sync_message(&mut s2).is_none());
1643
+
1644
+ // Neither has the other's data
1645
+ assert!(doc1.get(crate::ROOT, "y").unwrap().is_none());
1646
+ assert!(doc2.get(crate::ROOT, "x").unwrap().is_none());
1647
+ }
1648
+ }
1649
+
1650
+ #[test]
1651
+ fn read_only_peer_new_changes_between_sync_rounds() {
1652
+ // After an initial sync converges, the read-only peer makes new local
1653
+ // changes and syncs again. The new changes should flow to the other peer.
1654
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1655
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1656
+
1657
+ doc1.put(crate::ROOT, "round1", "from_doc1").unwrap();
1658
+ doc1.commit();
1659
+ doc2.put(crate::ROOT, "round1", "from_doc2").unwrap();
1660
+ doc2.commit();
1661
+
1662
+ let mut s1 = State::new_read_only();
1663
+ let mut s2 = State::new();
1664
+
1665
+ // First sync round
1666
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1667
+ assert!(doc2.get(crate::ROOT, "round1").unwrap().is_some());
1668
+
1669
+ // doc1 makes new changes
1670
+ doc1.put(crate::ROOT, "round2", "new_from_doc1").unwrap();
1671
+ doc1.commit();
1672
+
1673
+ // doc2 also makes new changes
1674
+ doc2.put(crate::ROOT, "round2", "new_from_doc2").unwrap();
1675
+ doc2.commit();
1676
+
1677
+ // Second sync round
1678
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1679
+
1680
+ // doc2 should have doc1's new changes
1681
+ assert_eq!(
1682
+ doc2.get(crate::ROOT, "round2").unwrap().unwrap().0.to_str(),
1683
+ // doc2 has both values as a conflict, but the winning value depends on actor ordering;
1684
+ // just check the key exists
1685
+ doc2.get(crate::ROOT, "round2").unwrap().unwrap().0.to_str(),
1686
+ );
1687
+ // Verify doc2 has received "new_from_doc1" somewhere in the conflicts
1688
+ let all_values: Vec<_> = doc2
1689
+ .get_all(crate::ROOT, "round2")
1690
+ .unwrap()
1691
+ .into_iter()
1692
+ .map(|(v, _)| v.into_string().unwrap())
1693
+ .collect();
1694
+ assert!(all_values.contains(&"new_from_doc1".to_string()));
1695
+ assert!(all_values.contains(&"new_from_doc2".to_string()));
1696
+
1697
+ // doc1 should still NOT have doc2's changes
1698
+ assert!(
1699
+ doc1.get(crate::ROOT, "from_doc2").is_err()
1700
+ || doc1.get(crate::ROOT, "from_doc2").unwrap().is_none()
1701
+ );
1702
+ let doc1_round2_values: Vec<_> = doc1
1703
+ .get_all(crate::ROOT, "round2")
1704
+ .unwrap()
1705
+ .into_iter()
1706
+ .map(|(v, _)| v.into_string().unwrap())
1707
+ .collect();
1708
+ assert_eq!(doc1_round2_values, vec!["new_from_doc1".to_string()]);
1709
+ }
1710
+
1711
+ #[test]
1712
+ fn read_only_peer_concurrent_changes_during_sync() {
1713
+ // Like in_flight_logic_should_not_sabotage_concurrent_changes but with
1714
+ // a read-only peer. The read-only peer makes a change after receiving a
1715
+ // message but before the sync loop completes.
1716
+ for _ in 0..300 {
1717
+ let mut doc1 =
1718
+ crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1719
+ let mut doc2 =
1720
+ crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1721
+ let mut s1 = State::new_read_only();
1722
+ let mut s2 = State::new();
1723
+
1724
+ // get them in sync (doc1 read-only, so only doc2 gets doc1's changes)
1725
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1726
+
1727
+ // doc2 makes a change
1728
+ doc2.put(crate::ROOT, "x", 0).unwrap();
1729
+
1730
+ // generate + receive one message from doc2 to doc1
1731
+ let msg = doc2.sync().generate_sync_message(&mut s2).unwrap();
1732
+ doc1.sync().receive_sync_message(&mut s1, msg).unwrap();
1733
+
1734
+ // before sending anything back, doc1 (read-only) makes a local change
1735
+ doc1.put(crate::ROOT, "y", 1).unwrap();
1736
+
1737
+ // now synchronize the rest
1738
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
1739
+
1740
+ // doc2 should have doc1's change
1741
+ assert!(doc2.get(crate::ROOT, "y").unwrap().is_some());
1742
+ // doc1 should NOT have doc2's change
1743
+ assert!(doc1.get(crate::ROOT, "x").unwrap().is_none());
1744
+ }
1745
+ }
1746
+
1747
+ #[test]
1748
+ fn read_only_publisher_to_multiple_consumers() {
1749
+ // R (read-only) publishes to A and B separately.
1750
+ // A makes its own changes. Then B syncs with R again.
1751
+ // B should NOT get A's changes through R (R never accepted them).
1752
+ let mut r = crate::AutoCommit::new().with_actor(ActorId::try_from("aaaaaa").unwrap());
1753
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("bbbbbb").unwrap());
1754
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("cccccc").unwrap());
1755
+
1756
+ r.put(crate::ROOT, "from_r", "hello").unwrap();
1757
+ r.commit();
1758
+
1759
+ // R syncs with A (read-only: A gets R's changes, R ignores A's)
1760
+ let mut sr_a = State::new_read_only();
1761
+ let mut sa_r = State::new();
1762
+ sync(&mut r, &mut a, &mut sr_a, &mut sa_r);
1763
+ assert!(a.get(crate::ROOT, "from_r").unwrap().is_some());
1764
+
1765
+ // A makes its own changes
1766
+ a.put(crate::ROOT, "from_a", "world").unwrap();
1767
+ a.commit();
1768
+
1769
+ // A syncs with R — R should ignore A's changes
1770
+ sync(&mut r, &mut a, &mut sr_a, &mut sa_r);
1771
+ assert!(r.get(crate::ROOT, "from_a").unwrap().is_none());
1772
+
1773
+ // R syncs with B (read-only: B gets R's changes, R ignores B's)
1774
+ let mut sr_b = State::new_read_only();
1775
+ let mut sb_r = State::new();
1776
+ sync(&mut r, &mut b, &mut sr_b, &mut sb_r);
1777
+
1778
+ // B should have R's changes but NOT A's
1779
+ assert!(b.get(crate::ROOT, "from_r").unwrap().is_some());
1780
+ assert!(b.get(crate::ROOT, "from_a").unwrap().is_none());
1781
+ }
1782
+
1783
+ #[test]
1784
+ fn triangle_changes_arrive_via_two_paths() {
1785
+ // R (read-only) makes changes, syncs with A. A gets R's changes.
1786
+ // A syncs with B (read-write). B gets R's changes via A.
1787
+ // Now B syncs with R directly. R should recognize B already has its
1788
+ // changes (via bloom filter) and not redundantly send them.
1789
+ // The protocol should converge.
1790
+ let mut r = crate::AutoCommit::new().with_actor(ActorId::try_from("aaaaaa").unwrap());
1791
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("bbbbbb").unwrap());
1792
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("cccccc").unwrap());
1793
+
1794
+ r.put(crate::ROOT, "from_r", "hello").unwrap();
1795
+ r.commit();
1796
+ a.put(crate::ROOT, "from_a", "world").unwrap();
1797
+ a.commit();
1798
+
1799
+ // R syncs with A (read-only)
1800
+ let mut sr_a = State::new_read_only();
1801
+ let mut sa_r = State::new();
1802
+ sync(&mut r, &mut a, &mut sr_a, &mut sa_r);
1803
+
1804
+ // A now has R's changes
1805
+ assert!(a.get(crate::ROOT, "from_r").unwrap().is_some());
1806
+
1807
+ // A syncs with B (both read-write)
1808
+ let mut sa_b = State::new();
1809
+ let mut sb_a = State::new();
1810
+ sync(&mut a, &mut b, &mut sa_b, &mut sb_a);
1811
+
1812
+ // B now has R's changes (received via A)
1813
+ assert!(b.get(crate::ROOT, "from_r").unwrap().is_some());
1814
+ assert!(b.get(crate::ROOT, "from_a").unwrap().is_some());
1815
+
1816
+ // Now B syncs with R directly
1817
+ let mut sr_b = State::new_read_only();
1818
+ let mut sb_r = State::new();
1819
+ sync(&mut r, &mut b, &mut sr_b, &mut sb_r);
1820
+
1821
+ // R should still only have its own changes
1822
+ assert!(r.get(crate::ROOT, "from_a").unwrap().is_none());
1823
+
1824
+ // B should still have everything (R's changes aren't duplicated, just confirmed)
1825
+ assert!(b.get(crate::ROOT, "from_r").unwrap().is_some());
1826
+ assert!(b.get(crate::ROOT, "from_a").unwrap().is_some());
1827
+ }
1828
+
1829
+ #[test]
1830
+ fn read_only_fully_connected_triangle() {
1831
+ // Three peers: A, B, R. R is read-only toward both A and B.
1832
+ // All three have their own changes. Each pair syncs.
1833
+ // A and B should converge to having A's + B's + R's changes.
1834
+ // R should only have its own changes.
1835
+ let mut r = crate::AutoCommit::new().with_actor(ActorId::try_from("aaaaaa").unwrap());
1836
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("bbbbbb").unwrap());
1837
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("cccccc").unwrap());
1838
+
1839
+ r.put(crate::ROOT, "from_r", "r_val").unwrap();
1840
+ r.commit();
1841
+ a.put(crate::ROOT, "from_a", "a_val").unwrap();
1842
+ a.commit();
1843
+ b.put(crate::ROOT, "from_b", "b_val").unwrap();
1844
+ b.commit();
1845
+
1846
+ let r_heads = r.get_heads();
1847
+
1848
+ // R syncs with A (read-only)
1849
+ let mut sr_a = State::new_read_only();
1850
+ let mut sa_r = State::new();
1851
+ sync(&mut r, &mut a, &mut sr_a, &mut sa_r);
1852
+
1853
+ // R syncs with B (read-only)
1854
+ let mut sr_b = State::new_read_only();
1855
+ let mut sb_r = State::new();
1856
+ sync(&mut r, &mut b, &mut sr_b, &mut sb_r);
1857
+
1858
+ // A and B each have R's changes via separate sync sessions
1859
+ assert!(a.get(crate::ROOT, "from_r").unwrap().is_some());
1860
+ assert!(b.get(crate::ROOT, "from_r").unwrap().is_some());
1861
+
1862
+ // A syncs with B (read-write) — both already have R's changes
1863
+ let mut sa_b = State::new();
1864
+ let mut sb_a = State::new();
1865
+ sync(&mut a, &mut b, &mut sa_b, &mut sb_a);
1866
+
1867
+ // A and B should have all three sets of changes
1868
+ assert!(a.get(crate::ROOT, "from_a").unwrap().is_some());
1869
+ assert!(a.get(crate::ROOT, "from_b").unwrap().is_some());
1870
+ assert!(a.get(crate::ROOT, "from_r").unwrap().is_some());
1871
+ assert!(b.get(crate::ROOT, "from_a").unwrap().is_some());
1872
+ assert!(b.get(crate::ROOT, "from_b").unwrap().is_some());
1873
+ assert!(b.get(crate::ROOT, "from_r").unwrap().is_some());
1874
+
1875
+ // A and B should have the same heads
1876
+ assert_eq!(a.get_heads(), b.get_heads());
1877
+
1878
+ // R should only have its own changes
1879
+ assert_eq!(r.get_heads(), r_heads);
1880
+ assert!(r.get(crate::ROOT, "from_a").unwrap().is_none());
1881
+ assert!(r.get(crate::ROOT, "from_b").unwrap().is_none());
1882
+ }
1883
+
1884
+ #[test]
1885
+ fn stale_shared_heads_after_read_only_sync() {
1886
+ // R (read-only) syncs with A. Then A syncs with B (B gets everything).
1887
+ // Now B syncs with R using a fresh State. R tries to send its changes
1888
+ // to B, but B already has them (received via A). The bloom filter
1889
+ // should prevent redundant sending and the protocol should converge.
1890
+ let mut r = crate::AutoCommit::new().with_actor(ActorId::try_from("aaaaaa").unwrap());
1891
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("bbbbbb").unwrap());
1892
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("cccccc").unwrap());
1893
+
1894
+ // R has several changes to make bloom filter interaction interesting
1895
+ for i in 0..10 {
1896
+ r.put(crate::ROOT, "counter", i as i64).unwrap();
1897
+ r.commit();
1898
+ }
1899
+ a.put(crate::ROOT, "from_a", "a_val").unwrap();
1900
+ a.commit();
1901
+
1902
+ // R syncs with A (read-only)
1903
+ let mut sr_a = State::new_read_only();
1904
+ let mut sa_r = State::new();
1905
+ sync(&mut r, &mut a, &mut sr_a, &mut sa_r);
1906
+
1907
+ // A now has R's 10 changes
1908
+ assert!(a.get(crate::ROOT, "counter").unwrap().is_some());
1909
+
1910
+ // A syncs with B — B gets everything (R's changes + A's changes)
1911
+ let mut sa_b = State::new();
1912
+ let mut sb_a = State::new();
1913
+ sync(&mut a, &mut b, &mut sa_b, &mut sb_a);
1914
+
1915
+ assert!(b.get(crate::ROOT, "counter").unwrap().is_some());
1916
+ assert!(b.get(crate::ROOT, "from_a").unwrap().is_some());
1917
+
1918
+ // Now B syncs directly with R (fresh sync state)
1919
+ let mut sr_b = State::new_read_only();
1920
+ let mut sb_r = State::new();
1921
+ sync(&mut r, &mut b, &mut sr_b, &mut sb_r);
1922
+
1923
+ // R should not have B's or A's changes
1924
+ assert!(r.get(crate::ROOT, "from_a").unwrap().is_none());
1925
+
1926
+ // B should still have everything
1927
+ assert!(b.get(crate::ROOT, "counter").unwrap().is_some());
1928
+ assert!(b.get(crate::ROOT, "from_a").unwrap().is_some());
1929
+ }
1930
+
1931
+ #[test]
1932
+ fn read_only_peer_receives_same_changes_from_two_peers() {
1933
+ // A and B sync with each other first (both have the same data).
1934
+ // Then A syncs with R (read-only), then B syncs with R (read-only).
1935
+ // R ignores changes both times. R's sync state must handle receiving
1936
+ // announcements about the same changes from two different peers.
1937
+ let mut r = crate::AutoCommit::new().with_actor(ActorId::try_from("aaaaaa").unwrap());
1938
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("bbbbbb").unwrap());
1939
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("cccccc").unwrap());
1940
+
1941
+ r.put(crate::ROOT, "from_r", "r_val").unwrap();
1942
+ r.commit();
1943
+ a.put(crate::ROOT, "from_a", "a_val").unwrap();
1944
+ a.commit();
1945
+ b.put(crate::ROOT, "from_b", "b_val").unwrap();
1946
+ b.commit();
1947
+
1948
+ // A and B sync — they now share A's + B's changes
1949
+ let mut sa_b = State::new();
1950
+ let mut sb_a = State::new();
1951
+ sync(&mut a, &mut b, &mut sa_b, &mut sb_a);
1952
+ assert_eq!(a.get_heads(), b.get_heads());
1953
+
1954
+ let r_heads = r.get_heads();
1955
+
1956
+ // A syncs with R (read-only) — R ignores A's+B's changes, A gets R's
1957
+ let mut sr_a = State::new_read_only();
1958
+ let mut sa_r = State::new();
1959
+ sync(&mut r, &mut a, &mut sr_a, &mut sa_r);
1960
+ assert!(a.get(crate::ROOT, "from_r").unwrap().is_some());
1961
+ assert_eq!(r.get_heads(), r_heads);
1962
+
1963
+ // B syncs with R (read-only) — R ignores the same changes again, B gets R's
1964
+ let mut sr_b = State::new_read_only();
1965
+ let mut sb_r = State::new();
1966
+ sync(&mut r, &mut b, &mut sr_b, &mut sb_r);
1967
+ assert!(b.get(crate::ROOT, "from_r").unwrap().is_some());
1968
+
1969
+ // R should still only have its own changes
1970
+ assert_eq!(r.get_heads(), r_heads);
1971
+ assert!(r.get(crate::ROOT, "from_a").unwrap().is_none());
1972
+ assert!(r.get(crate::ROOT, "from_b").unwrap().is_none());
1973
+
1974
+ // R makes a new change
1975
+ r.put(crate::ROOT, "from_r_2", "new").unwrap();
1976
+ r.commit();
1977
+
1978
+ // Sync R with A again — A should get the new change
1979
+ sync(&mut r, &mut a, &mut sr_a, &mut sa_r);
1980
+ assert!(a.get(crate::ROOT, "from_r_2").unwrap().is_some());
1981
+
1982
+ // Sync R with B again — B should also get the new change
1983
+ sync(&mut r, &mut b, &mut sr_b, &mut sb_r);
1984
+ assert!(b.get(crate::ROOT, "from_r_2").unwrap().is_some());
1985
+ }
1986
+
1987
+ #[test]
1988
+ fn switch_read_only_to_read_write_mid_session() {
1989
+ // A starts read-only, syncs with B (A sends changes, ignores B's).
1990
+ // Then A switches to read-write and syncs again.
1991
+ // A should now receive B's changes despite B having previously sent
1992
+ // them (the sent_hashes problem).
1993
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
1994
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
1995
+
1996
+ a.put(crate::ROOT, "from_a", "hello").unwrap();
1997
+ a.commit();
1998
+ b.put(crate::ROOT, "from_b", "world").unwrap();
1999
+ b.commit();
2000
+
2001
+ let mut sa = State::new_read_only();
2002
+ let mut sb = State::new();
2003
+
2004
+ // First sync: A is read-only
2005
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2006
+ assert!(b.get(crate::ROOT, "from_a").unwrap().is_some());
2007
+ assert!(a.get(crate::ROOT, "from_b").unwrap().is_none());
2008
+
2009
+ // Switch A to read-write
2010
+ sa.set_read_only(false);
2011
+
2012
+ // Second sync: A should now receive B's changes
2013
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2014
+ assert!(a.get(crate::ROOT, "from_b").unwrap().is_some());
2015
+
2016
+ // Both should have the same heads now
2017
+ assert_eq!(a.get_heads(), b.get_heads());
2018
+ }
2019
+
2020
+ #[test]
2021
+ fn switch_read_write_to_read_only_mid_session() {
2022
+ // A and B sync normally (both read-write). Then A switches to
2023
+ // read-only. B makes new changes. A should not receive them.
2024
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2025
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2026
+
2027
+ a.put(crate::ROOT, "from_a", "hello").unwrap();
2028
+ a.commit();
2029
+ b.put(crate::ROOT, "from_b", "world").unwrap();
2030
+ b.commit();
2031
+
2032
+ let mut sa = State::new();
2033
+ let mut sb = State::new();
2034
+
2035
+ // First sync: both read-write
2036
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2037
+ assert_eq!(a.get_heads(), b.get_heads());
2038
+
2039
+ // Switch A to read-only
2040
+ sa.set_read_only(true);
2041
+
2042
+ // B makes new changes
2043
+ b.put(crate::ROOT, "new_from_b", "secret").unwrap();
2044
+ b.commit();
2045
+
2046
+ // A also makes a new change
2047
+ a.put(crate::ROOT, "new_from_a", "published").unwrap();
2048
+ a.commit();
2049
+
2050
+ // Second sync: A is now read-only
2051
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2052
+
2053
+ // B should have A's new change
2054
+ assert!(b.get(crate::ROOT, "new_from_a").unwrap().is_some());
2055
+ // A should NOT have B's new change
2056
+ assert!(a.get(crate::ROOT, "new_from_b").unwrap().is_none());
2057
+ }
2058
+
2059
+ #[test]
2060
+ fn switch_read_only_to_read_write_with_multiple_rounds() {
2061
+ // A starts read-only, syncs multiple rounds with B (B makes changes
2062
+ // each round). A switches to read-write. A should receive ALL of B's
2063
+ // accumulated changes.
2064
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2065
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2066
+
2067
+ a.put(crate::ROOT, "from_a", "initial").unwrap();
2068
+ a.commit();
2069
+
2070
+ let mut sa = State::new_read_only();
2071
+ let mut sb = State::new();
2072
+
2073
+ // Round 1: B has changes, A is read-only
2074
+ b.put(crate::ROOT, "round1", "from_b").unwrap();
2075
+ b.commit();
2076
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2077
+ assert!(a.get(crate::ROOT, "round1").unwrap().is_none());
2078
+
2079
+ // Round 2: B has more changes, A still read-only
2080
+ b.put(crate::ROOT, "round2", "from_b").unwrap();
2081
+ b.commit();
2082
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2083
+ assert!(a.get(crate::ROOT, "round2").unwrap().is_none());
2084
+
2085
+ // Round 3: B has even more changes, A still read-only
2086
+ b.put(crate::ROOT, "round3", "from_b").unwrap();
2087
+ b.commit();
2088
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2089
+ assert!(a.get(crate::ROOT, "round3").unwrap().is_none());
2090
+
2091
+ // Switch A to read-write
2092
+ sa.set_read_only(false);
2093
+
2094
+ // Sync again — A should receive ALL of B's changes from all rounds
2095
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2096
+ assert!(a.get(crate::ROOT, "round1").unwrap().is_some());
2097
+ assert!(a.get(crate::ROOT, "round2").unwrap().is_some());
2098
+ assert!(a.get(crate::ROOT, "round3").unwrap().is_some());
2099
+
2100
+ assert_eq!(a.get_heads(), b.get_heads());
2101
+ }
2102
+
2103
+ #[test]
2104
+ fn toggle_read_only_multiple_times() {
2105
+ // Rapidly toggle read-only on and off, making changes between each toggle.
2106
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2107
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2108
+
2109
+ let mut sa = State::new_read_only();
2110
+ let mut sb = State::new();
2111
+
2112
+ // Round 1: A read-only, B makes changes
2113
+ b.put(crate::ROOT, "b1", "val").unwrap();
2114
+ b.commit();
2115
+ a.put(crate::ROOT, "a1", "val").unwrap();
2116
+ a.commit();
2117
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2118
+ assert!(b.get(crate::ROOT, "a1").unwrap().is_some());
2119
+ assert!(a.get(crate::ROOT, "b1").unwrap().is_none());
2120
+
2121
+ // Round 2: switch to read-write
2122
+ sa.set_read_only(false);
2123
+ b.put(crate::ROOT, "b2", "val").unwrap();
2124
+ b.commit();
2125
+ a.put(crate::ROOT, "a2", "val").unwrap();
2126
+ a.commit();
2127
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2128
+ assert!(a.get(crate::ROOT, "b1").unwrap().is_some());
2129
+ assert!(a.get(crate::ROOT, "b2").unwrap().is_some());
2130
+ assert!(b.get(crate::ROOT, "a2").unwrap().is_some());
2131
+
2132
+ // Round 3: switch back to read-only
2133
+ sa.set_read_only(true);
2134
+ b.put(crate::ROOT, "b3", "val").unwrap();
2135
+ b.commit();
2136
+ a.put(crate::ROOT, "a3", "val").unwrap();
2137
+ a.commit();
2138
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2139
+ assert!(b.get(crate::ROOT, "a3").unwrap().is_some());
2140
+ assert!(a.get(crate::ROOT, "b3").unwrap().is_none());
2141
+
2142
+ // Round 4: switch to read-write again
2143
+ sa.set_read_only(false);
2144
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2145
+ assert!(a.get(crate::ROOT, "b3").unwrap().is_some());
2146
+ assert_eq!(a.get_heads(), b.get_heads());
2147
+ }
2148
+
2149
+ #[test]
2150
+ fn peer_discovers_remote_read_only_status() {
2151
+ // After exchanging messages, B should discover that A is read-only.
2152
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2153
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2154
+
2155
+ a.put(crate::ROOT, "from_a", "hello").unwrap();
2156
+ a.commit();
2157
+ b.put(crate::ROOT, "from_b", "world").unwrap();
2158
+ b.commit();
2159
+
2160
+ let mut sa = State::new_read_only();
2161
+ let mut sb = State::new();
2162
+
2163
+ // Before any exchange, B doesn't know A's status
2164
+ assert!(!sb.is_peer_read_only());
2165
+
2166
+ // A generates a message (includes ReadOnly capability)
2167
+ let msg = a.sync().generate_sync_message(&mut sa).unwrap();
2168
+
2169
+ // B receives it and discovers A is read-only
2170
+ b.sync().receive_sync_message(&mut sb, msg).unwrap();
2171
+ assert!(sb.is_peer_read_only());
2172
+
2173
+ // Complete the sync
2174
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2175
+ assert!(sb.is_peer_read_only());
2176
+
2177
+ // A switches to read-write
2178
+ sa.set_read_only(false);
2179
+
2180
+ // After exchanging messages, B discovers A is no longer read-only
2181
+ let msg = a.sync().generate_sync_message(&mut sa).unwrap();
2182
+ b.sync().receive_sync_message(&mut sb, msg).unwrap();
2183
+ assert!(!sb.is_peer_read_only());
2184
+ }
2185
+
2186
+ #[test]
2187
+ fn changes_not_sent_to_read_only_peer() {
2188
+ // B should not send changes to A when B knows A is read-only.
2189
+ // This saves bandwidth.
2190
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2191
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2192
+
2193
+ b.put(crate::ROOT, "from_b", "world").unwrap();
2194
+ b.commit();
2195
+
2196
+ let mut sa = State::new_read_only();
2197
+ let mut sb = State::new();
2198
+
2199
+ // Exchange initial messages so B discovers A is read-only
2200
+ let msg_a = a.sync().generate_sync_message(&mut sa).unwrap();
2201
+ b.sync().receive_sync_message(&mut sb, msg_a).unwrap();
2202
+ assert!(sb.is_peer_read_only());
2203
+
2204
+ // B generates a response — should have no changes since A is read-only
2205
+ let msg_b = b.sync().generate_sync_message(&mut sb).unwrap();
2206
+ assert!(msg_b.changes.is_empty());
2207
+
2208
+ // Complete the sync
2209
+ a.sync().receive_sync_message(&mut sa, msg_b).unwrap();
2210
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2211
+
2212
+ // A still doesn't have B's changes (read-only + B didn't even send them)
2213
+ assert!(a.get(crate::ROOT, "from_b").unwrap().is_none());
2214
+
2215
+ // Now A switches to read-write
2216
+ sa.set_read_only(false);
2217
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2218
+
2219
+ // A should now have B's changes
2220
+ assert!(a.get(crate::ROOT, "from_b").unwrap().is_some());
2221
+ assert_eq!(a.get_heads(), b.get_heads());
2222
+ }
2223
+
2224
+ #[test]
2225
+ fn generate_message_after_set_read_only_even_with_in_flight() {
2226
+ // After calling generate_sync_message (setting in_flight=true),
2227
+ // then set_read_only, the next generate_sync_message must still
2228
+ // produce a message so the peer learns about the mode change.
2229
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2230
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2231
+
2232
+ a.put(crate::ROOT, "from_a", "hello").unwrap();
2233
+ a.commit();
2234
+ b.put(crate::ROOT, "from_b", "world").unwrap();
2235
+ b.commit();
2236
+
2237
+ let mut sa = State::new();
2238
+ let mut sb = State::new();
2239
+
2240
+ // Normal sync to get in sync
2241
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2242
+
2243
+ // B makes a new change and sends it to A
2244
+ b.put(crate::ROOT, "new_from_b", "secret").unwrap();
2245
+ b.commit();
2246
+ let msg_b = b.sync().generate_sync_message(&mut sb).unwrap();
2247
+ a.sync().receive_sync_message(&mut sa, msg_b).unwrap();
2248
+
2249
+ // A generates a response (sets in_flight=true)
2250
+ let msg = a.sync().generate_sync_message(&mut sa);
2251
+ assert!(msg.is_some());
2252
+ assert!(sa.in_flight);
2253
+
2254
+ // A switches to read-only BEFORE receiving B's next message
2255
+ sa.set_read_only(true);
2256
+
2257
+ // A must be able to generate another message to advertise ReadOnly
2258
+ let msg = a.sync().generate_sync_message(&mut sa);
2259
+ assert!(
2260
+ msg.is_some(),
2261
+ "should generate message after set_read_only even with prior in_flight"
2262
+ );
2263
+
2264
+ // Verify the message advertises ReadOnly
2265
+ let msg = msg.unwrap();
2266
+ assert!(msg.flags.unwrap().contains(MessageFlags::READ_ONLY));
2267
+ }
2268
+
2269
+ #[test]
2270
+ fn generate_message_after_set_read_only_false_even_with_in_flight() {
2271
+ // Same as above but switching from read-only to read-write.
2272
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2273
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2274
+
2275
+ a.put(crate::ROOT, "from_a", "hello").unwrap();
2276
+ a.commit();
2277
+
2278
+ let mut sa = State::new_read_only();
2279
+ let mut sb = State::new();
2280
+
2281
+ // Sync while read-only
2282
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2283
+
2284
+ // B makes a change
2285
+ b.put(crate::ROOT, "from_b", "world").unwrap();
2286
+ b.commit();
2287
+
2288
+ // A generates (sets in_flight)
2289
+ let _msg = a.sync().generate_sync_message(&mut sa);
2290
+ // Force some state by receiving a message
2291
+ let msg_b = b.sync().generate_sync_message(&mut sb).unwrap();
2292
+ a.sync().receive_sync_message(&mut sa, msg_b).unwrap();
2293
+ let _ = a.sync().generate_sync_message(&mut sa);
2294
+
2295
+ // Now switch to read-write
2296
+ sa.set_read_only(false);
2297
+
2298
+ // Must generate a message with SyncReset
2299
+ let msg = a.sync().generate_sync_message(&mut sa);
2300
+ assert!(
2301
+ msg.is_some(),
2302
+ "should generate message after switching to read-write"
2303
+ );
2304
+ let msg = msg.unwrap();
2305
+ let flags = msg.flags.unwrap();
2306
+ assert!(flags.contains(MessageFlags::SYNC_RESET));
2307
+ assert!(!flags.contains(MessageFlags::READ_ONLY));
2308
+ }
2309
+
2310
+ #[test]
2311
+ fn both_toggle_read_only_to_read_write_simultaneously() {
2312
+ // Both peers start read-only with their own changes, sync (exchanging
2313
+ // heads but not changes), then both switch to read-write simultaneously.
2314
+ // After syncing again, both should have each other's changes.
2315
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2316
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2317
+
2318
+ doc1.put(crate::ROOT, "from_doc1", "hello").unwrap();
2319
+ doc1.commit();
2320
+ doc2.put(crate::ROOT, "from_doc2", "world").unwrap();
2321
+ doc2.commit();
2322
+
2323
+ let mut s1 = State::new_read_only();
2324
+ let mut s2 = State::new_read_only();
2325
+
2326
+ // Sync while both read-only — neither gets the other's changes
2327
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
2328
+ assert!(doc1.get(crate::ROOT, "from_doc2").unwrap().is_none());
2329
+ assert!(doc2.get(crate::ROOT, "from_doc1").unwrap().is_none());
2330
+
2331
+ // Both switch to read-write simultaneously
2332
+ s1.set_read_only(false);
2333
+ s2.set_read_only(false);
2334
+
2335
+ // Sync again — both should exchange and apply changes
2336
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
2337
+
2338
+ assert!(doc1.get(crate::ROOT, "from_doc2").unwrap().is_some());
2339
+ assert!(doc2.get(crate::ROOT, "from_doc1").unwrap().is_some());
2340
+ assert_eq!(doc1.get_heads(), doc2.get_heads());
2341
+ }
2342
+
2343
+ #[test]
2344
+ fn both_toggle_read_only_to_read_write_with_new_changes() {
2345
+ // Both peers start read-only, sync, then both switch to read-write
2346
+ // and make additional changes before syncing.
2347
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2348
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2349
+
2350
+ doc1.put(crate::ROOT, "original_1", "v1").unwrap();
2351
+ doc1.commit();
2352
+ doc2.put(crate::ROOT, "original_2", "v2").unwrap();
2353
+ doc2.commit();
2354
+
2355
+ let mut s1 = State::new_read_only();
2356
+ let mut s2 = State::new_read_only();
2357
+
2358
+ // Sync while both read-only
2359
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
2360
+
2361
+ // Both switch to read-write
2362
+ s1.set_read_only(false);
2363
+ s2.set_read_only(false);
2364
+
2365
+ // Both make NEW changes after switching
2366
+ doc1.put(crate::ROOT, "new_1", "after_switch").unwrap();
2367
+ doc1.commit();
2368
+ doc2.put(crate::ROOT, "new_2", "after_switch").unwrap();
2369
+ doc2.commit();
2370
+
2371
+ // Sync — should get both original AND new changes from each other
2372
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
2373
+
2374
+ assert!(doc1.get(crate::ROOT, "original_2").unwrap().is_some());
2375
+ assert!(doc1.get(crate::ROOT, "new_2").unwrap().is_some());
2376
+ assert!(doc2.get(crate::ROOT, "original_1").unwrap().is_some());
2377
+ assert!(doc2.get(crate::ROOT, "new_1").unwrap().is_some());
2378
+ assert_eq!(doc1.get_heads(), doc2.get_heads());
2379
+ }
2380
+
2381
+ #[test]
2382
+ fn both_toggle_after_multiple_read_only_rounds() {
2383
+ // Both peers are read-only for several rounds, making changes each
2384
+ // round. Then both switch to read-write. All accumulated changes
2385
+ // from all rounds should be exchanged.
2386
+ let mut doc1 = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2387
+ let mut doc2 = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2388
+
2389
+ let mut s1 = State::new_read_only();
2390
+ let mut s2 = State::new_read_only();
2391
+
2392
+ // Several rounds of read-only sync with changes each round
2393
+ for i in 0..5 {
2394
+ doc1.put(crate::ROOT, format!("doc1_r{i}"), i as i64)
2395
+ .unwrap();
2396
+ doc1.commit();
2397
+ doc2.put(crate::ROOT, format!("doc2_r{i}"), i as i64)
2398
+ .unwrap();
2399
+ doc2.commit();
2400
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
2401
+ }
2402
+
2403
+ // Neither has the other's changes from any round
2404
+ for i in 0..5 {
2405
+ assert!(doc1
2406
+ .get(crate::ROOT, format!("doc2_r{i}"))
2407
+ .unwrap()
2408
+ .is_none());
2409
+ assert!(doc2
2410
+ .get(crate::ROOT, format!("doc1_r{i}"))
2411
+ .unwrap()
2412
+ .is_none());
2413
+ }
2414
+
2415
+ // Both switch to read-write
2416
+ s1.set_read_only(false);
2417
+ s2.set_read_only(false);
2418
+
2419
+ // Sync — all accumulated changes should flow
2420
+ sync(&mut doc1, &mut doc2, &mut s1, &mut s2);
2421
+
2422
+ for i in 0..5 {
2423
+ assert!(
2424
+ doc1.get(crate::ROOT, format!("doc2_r{i}"))
2425
+ .unwrap()
2426
+ .is_some(),
2427
+ "doc1 missing doc2_r{i}"
2428
+ );
2429
+ assert!(
2430
+ doc2.get(crate::ROOT, format!("doc1_r{i}"))
2431
+ .unwrap()
2432
+ .is_some(),
2433
+ "doc2 missing doc1_r{i}"
2434
+ );
2435
+ }
2436
+ assert_eq!(doc1.get_heads(), doc2.get_heads());
2437
+ }
2438
+
2439
+ #[test]
2440
+ fn switch_to_read_write_with_old_peer() {
2441
+ // Simulates switching from read-only to read-write when the remote
2442
+ // peer is an old implementation that doesn't understand SyncReset.
2443
+ // Old peers have their_capabilities = None. The fallback sends empty
2444
+ // heads, which triggers the old "peer lost all data" reset path.
2445
+ let mut a = crate::AutoCommit::new().with_actor(ActorId::try_from("abc123").unwrap());
2446
+ let mut b = crate::AutoCommit::new().with_actor(ActorId::try_from("def456").unwrap());
2447
+
2448
+ a.put(crate::ROOT, "from_a", "hello").unwrap();
2449
+ a.commit();
2450
+ b.put(crate::ROOT, "from_b", "world").unwrap();
2451
+ b.commit();
2452
+
2453
+ let mut sa = State::new_read_only();
2454
+ let mut sb = State::new();
2455
+
2456
+ // Sync while read-only
2457
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2458
+ assert!(a.get(crate::ROOT, "from_b").unwrap().is_none());
2459
+ assert!(b.get(crate::ROOT, "from_a").unwrap().is_some());
2460
+
2461
+ // Simulate an old peer by clearing their_capabilities before switching.
2462
+ // set_read_only preserves their_capabilities, so we clear after.
2463
+ sa.set_read_only(false);
2464
+ sa.their_capabilities = None;
2465
+
2466
+ // The first message should have empty heads (old peer fallback)
2467
+ let msg = a.sync().generate_sync_message(&mut sa).unwrap();
2468
+ assert!(msg.heads.is_empty(), "should send empty heads for old peer");
2469
+ assert!(
2470
+ !msg.flags.unwrap().contains(MessageFlags::SYNC_RESET),
2471
+ "should not include SyncReset for old peer"
2472
+ );
2473
+
2474
+ // Complete the sync — old peer sees empty heads, clears sent_hashes
2475
+ b.sync().receive_sync_message(&mut sb, msg).unwrap();
2476
+ sync(&mut a, &mut b, &mut sa, &mut sb);
2477
+
2478
+ // A should now have B's changes
2479
+ assert!(a.get(crate::ROOT, "from_b").unwrap().is_some());
2480
+ assert_eq!(a.get_heads(), b.get_heads());
2481
+ }
2482
+ }