@zio.dev/zio-blocks 0.0.28 → 0.0.29

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.
@@ -39,13 +39,13 @@ The bidirectional data flow looks like this:
39
39
  `As` is part of `zio-blocks-schema`:
40
40
 
41
41
  ```scala
42
- libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.28"
42
+ libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.29"
43
43
  ```
44
44
 
45
45
  For Scala.js and Scala Native, use `%%%`:
46
46
 
47
47
  ```scala
48
- libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.28"
48
+ libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.29"
49
49
  ```
50
50
 
51
51
  Supported Scala versions: 2.13.x and 3.x.
@@ -240,7 +240,7 @@ trait As[A, B] {
240
240
 
241
241
  ```scala
242
242
  val revAs: As[LongBox, IntBox] = boxAs.reverse
243
- // revAs: As[LongBox, IntBox] = zio.blocks.schema.As$$anon$1@15f56a1f
243
+ // revAs: As[LongBox, IntBox] = zio.blocks.schema.As$$anon$1@29407f04
244
244
 
245
245
  revAs.into(LongBox(5L))
246
246
  // res11: Either[SchemaError, IntBox] = Right(IntBox(5))
@@ -302,7 +302,7 @@ We import `As.reverseInto` and use it to obtain the reverse `Into[Int, String]`:
302
302
  import As.reverseInto
303
303
 
304
304
  val intToStr: Into[Int, String] = reverseInto[String, Int]
305
- // intToStr: Into[Int, String] = zio.blocks.schema.AsLowPriorityImplicits$$Lambda$16607/0x00007f8eb27d4250@40672bcc
305
+ // intToStr: Into[Int, String] = zio.blocks.schema.AsLowPriorityImplicits$$Lambda$16771/0x00007f092a81f330@1cdf3706
306
306
  intToStr.into(42)
307
307
  // res14: Either[SchemaError, String] = Right("42")
308
308
  ```
@@ -69,13 +69,13 @@ Compare this to a manual implementation:
69
69
  `Into` is part of the `zio-blocks-schema` module:
70
70
 
71
71
  ```scala
72
- libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.28"
72
+ libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.29"
73
73
  ```
74
74
 
75
75
  For Scala.js:
76
76
 
77
77
  ```scala
78
- libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.28"
78
+ libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.29"
79
79
  ```
80
80
 
81
81
  Supported Scala versions: 2.13.x and 3.x.
@@ -70,13 +70,13 @@ val result: Either[OpticCheck, Seq[Boolean]] = combined.eval(alice)
70
70
  ## Installation
71
71
 
72
72
  ```scala
73
- libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.28"
73
+ libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.29"
74
74
  ```
75
75
 
76
76
  For cross-platform (Scala.js):
77
77
 
78
78
  ```scala
79
- libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.28"
79
+ libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.29"
80
80
  ```
81
81
 
82
82
  Supported Scala versions: 2.13.x and 3.x.
@@ -107,7 +107,7 @@ trait Deriver[TC[_]] {
107
107
  def deriveRecord[F[_, _], A](
108
108
  fields: IndexedSeq[Term[F, A, ?]],
109
109
  typeId: TypeId[A],
110
- binding: Binding[BindingType.Record, A],
110
+ binding: Binding.Record[A],
111
111
  doc: Doc,
112
112
  modifiers: Seq[Modifier.Reflect],
113
113
  defaultValue: Option[A],
@@ -194,7 +194,7 @@ object DeriveShow extends Deriver[Show] {
194
194
  override def derivePrimitive[A](
195
195
  primitiveType: PrimitiveType[A],
196
196
  typeId: TypeId[A],
197
- binding: Binding[BindingType.Primitive, A],
197
+ binding: Binding.Primitive[A],
198
198
  doc: Doc,
199
199
  modifiers: Seq[Modifier.Reflect],
200
200
  defaultValue: Option[A],
@@ -213,7 +213,7 @@ object DeriveShow extends Deriver[Show] {
213
213
  override def deriveRecord[F[_, _], A](
214
214
  fields: IndexedSeq[Term[F, A, ?]],
215
215
  typeId: TypeId[A],
216
- binding: Binding[BindingType.Record, A],
216
+ binding: Binding.Record[A],
217
217
  doc: Doc,
218
218
  modifiers: Seq[Modifier.Reflect],
219
219
  defaultValue: Option[A],
@@ -255,7 +255,7 @@ object DeriveShow extends Deriver[Show] {
255
255
  override def deriveVariant[F[_, _], A](
256
256
  cases: IndexedSeq[Term[F, A, ?]],
257
257
  typeId: TypeId[A],
258
- binding: Binding[BindingType.Variant, A],
258
+ binding: Binding.Variant[A],
259
259
  doc: Doc,
260
260
  modifiers: Seq[Modifier.Reflect],
261
261
  defaultValue: Option[A],
@@ -289,7 +289,7 @@ object DeriveShow extends Deriver[Show] {
289
289
  override def deriveSequence[F[_, _], C[_], A](
290
290
  element: Reflect[F, A],
291
291
  typeId: TypeId[C[A]],
292
- binding: Binding[BindingType.Seq[C], C[A]],
292
+ binding: Binding.Seq[C, A],
293
293
  doc: Doc,
294
294
  modifiers: Seq[Modifier.Reflect],
295
295
  defaultValue: Option[C[A]],
@@ -315,7 +315,7 @@ object DeriveShow extends Deriver[Show] {
315
315
  key: Reflect[F, K],
316
316
  value: Reflect[F, V],
317
317
  typeId: TypeId[M[K, V]],
318
- binding: Binding[BindingType.Map[M], M[K, V]],
318
+ binding: Binding.Map[M, K, V],
319
319
  doc: Doc,
320
320
  modifiers: Seq[Modifier.Reflect],
321
321
  defaultValue: Option[M[K, V]],
@@ -341,7 +341,7 @@ object DeriveShow extends Deriver[Show] {
341
341
  }
342
342
 
343
343
  override def deriveDynamic[F[_, _]](
344
- binding: Binding[BindingType.Dynamic, DynamicValue],
344
+ binding: Binding.Dynamic,
345
345
  doc: Doc,
346
346
  modifiers: Seq[Modifier.Reflect],
347
347
  defaultValue: Option[DynamicValue],
@@ -380,7 +380,7 @@ object DeriveShow extends Deriver[Show] {
380
380
  override def deriveWrapper[F[_, _], A, B](
381
381
  wrapped: Reflect[F, B],
382
382
  typeId: TypeId[A],
383
- binding: Binding[BindingType.Wrapper[A, B], A],
383
+ binding: Binding.Wrapper[A, B],
384
384
  doc: Doc,
385
385
  modifiers: Seq[Modifier.Reflect],
386
386
  defaultValue: Option[A],
@@ -414,7 +414,7 @@ When the derivation process encounters a primitive type (e.g., `String`, `Int`),
414
414
  def derivePrimitive[A](
415
415
  primitiveType: PrimitiveType[A],
416
416
  typeId: TypeId[A],
417
- binding: Binding[BindingType.Primitive, A],
417
+ binding: Binding.Primitive[A],
418
418
  doc: Doc,
419
419
  modifiers: Seq[Modifier.Reflect],
420
420
  defaultValue: Option[A],
@@ -444,7 +444,7 @@ When the derivation process encounters a record type (e.g., a case class), it ca
444
444
  def deriveRecord[F[_, _], A](
445
445
  fields: IndexedSeq[Term[F, A, ?]],
446
446
  typeId: TypeId[A],
447
- binding: Binding[BindingType.Record, A],
447
+ binding: Binding.Record[A],
448
448
  doc: Doc,
449
449
  modifiers: Seq[Modifier.Reflect],
450
450
  defaultValue: Option[A],
@@ -512,7 +512,7 @@ When the derivation process encounters a variant type (e.g., a sealed trait with
512
512
  def deriveVariant[F[_, _], A](
513
513
  cases: IndexedSeq[Term[F, A, ?]],
514
514
  typeId: TypeId[A],
515
- binding: Binding[BindingType.Variant, A],
515
+ binding: Binding.Variant[A],
516
516
  doc: Doc,
517
517
  modifiers: Seq[Modifier.Reflect],
518
518
  defaultValue: Option[A],
@@ -556,7 +556,7 @@ When the derivation process encounters a sequence type (e.g., `List[A]`), it cal
556
556
  def deriveSequence[F[_, _], C[_], A](
557
557
  element: Reflect[F, A],
558
558
  typeId: TypeId[C[A]],
559
- binding: Binding[BindingType.Seq[C], C[A]],
559
+ binding: Binding.Seq[C, A],
560
560
  doc: Doc,
561
561
  modifiers: Seq[Modifier.Reflect],
562
562
  defaultValue: Option[C[A]],
@@ -590,7 +590,7 @@ def deriveMap[F[_, _], M[_, _], K, V](
590
590
  key: Reflect[F, K],
591
591
  value: Reflect[F, V],
592
592
  typeId: TypeId[M[K, V]],
593
- binding: Binding[BindingType.Map[M], M[K, V]],
593
+ binding: Binding.Map[M, K, V],
594
594
  doc: Doc,
595
595
  modifiers: Seq[Modifier.Reflect],
596
596
  defaultValue: Option[M[K, V]],
@@ -620,11 +620,11 @@ The derivation process for maps is similar to sequences, but we have two child i
620
620
 
621
621
  ### Dynamic Derivation
622
622
 
623
- When the derivation process encounters a dynamic type (e.g., `DynamicValue`), it calls the `deriveDynamic` method of the `Deriver`. This method receives a `Binding[BindingType.Dynamic, DynamicValue]` representing the dynamic type, along with other metadata such as documentation, modifiers, default values, and examples:
623
+ When the derivation process encounters a dynamic type (e.g., `DynamicValue`), it calls the `deriveDynamic` method of the `Deriver`. This method receives a `Binding.Dynamic` representing the dynamic type, along with other metadata such as documentation, modifiers, default values, and examples:
624
624
 
625
625
  ```scala
626
626
  def deriveDynamic[F[_, _]](
627
- binding: Binding[BindingType.Dynamic, DynamicValue],
627
+ binding: Binding.Dynamic,
628
628
  doc: Doc,
629
629
  modifiers: Seq[Modifier.Reflect],
630
630
  defaultValue: Option[DynamicValue],
@@ -667,7 +667,7 @@ When the derivation process encounters a wrapper type (e.g., a value class, opaq
667
667
  def deriveWrapper[F[_, _], A, B](
668
668
  wrapped: Reflect[F, B],
669
669
  typeId: TypeId[A],
670
- binding: Binding[BindingType.Wrapper[A, B], A],
670
+ binding: Binding.Wrapper[A, B],
671
671
  doc: Doc,
672
672
  modifiers: Seq[Modifier.Reflect],
673
673
  defaultValue: Option[A],
@@ -875,7 +875,7 @@ object DeriveGen extends Deriver[Gen] {
875
875
  override def derivePrimitive[A](
876
876
  primitiveType: PrimitiveType[A],
877
877
  typeId: TypeId[A],
878
- binding: Binding[BindingType.Primitive, A],
878
+ binding: Binding.Primitive[A],
879
879
  doc: Doc,
880
880
  modifiers: Seq[Modifier.Reflect],
881
881
  defaultValue: Option[A],
@@ -913,7 +913,7 @@ object DeriveGen extends Deriver[Gen] {
913
913
  override def deriveRecord[F[_, _], A](
914
914
  fields: IndexedSeq[Term[F, A, ?]],
915
915
  typeId: TypeId[A],
916
- binding: Binding[BindingType.Record, A],
916
+ binding: Binding.Record[A],
917
917
  doc: Doc,
918
918
  modifiers: Seq[Modifier.Reflect],
919
919
  defaultValue: Option[A],
@@ -957,7 +957,7 @@ object DeriveGen extends Deriver[Gen] {
957
957
  override def deriveVariant[F[_, _], A](
958
958
  cases: IndexedSeq[Term[F, A, ?]],
959
959
  typeId: TypeId[A],
960
- binding: Binding[BindingType.Variant, A],
960
+ binding: Binding.Variant[A],
961
961
  doc: Doc,
962
962
  modifiers: Seq[Modifier.Reflect],
963
963
  defaultValue: Option[A],
@@ -990,7 +990,7 @@ object DeriveGen extends Deriver[Gen] {
990
990
  override def deriveSequence[F[_, _], C[_], A](
991
991
  element: Reflect[F, A],
992
992
  typeId: TypeId[C[A]],
993
- binding: Binding[BindingType.Seq[C], C[A]],
993
+ binding: Binding.Seq[C, A],
994
994
  doc: Doc,
995
995
  modifiers: Seq[Modifier.Reflect],
996
996
  defaultValue: Option[C[A]],
@@ -1033,7 +1033,7 @@ object DeriveGen extends Deriver[Gen] {
1033
1033
  key: Reflect[F, K],
1034
1034
  value: Reflect[F, V],
1035
1035
  typeId: TypeId[M[K, V]],
1036
- binding: Binding[BindingType.Map[M], M[K, V]],
1036
+ binding: Binding.Map[M, K, V],
1037
1037
  doc: Doc,
1038
1038
  modifiers: Seq[Modifier.Reflect],
1039
1039
  defaultValue: Option[M[K, V]],
@@ -1069,7 +1069,7 @@ object DeriveGen extends Deriver[Gen] {
1069
1069
  * content.
1070
1070
  */
1071
1071
  override def deriveDynamic[F[_, _]](
1072
- binding: Binding[BindingType.Dynamic, DynamicValue],
1072
+ binding: Binding.Dynamic,
1073
1073
  doc: Doc,
1074
1074
  modifiers: Seq[Modifier.Reflect],
1075
1075
  defaultValue: Option[DynamicValue],
@@ -1124,7 +1124,7 @@ object DeriveGen extends Deriver[Gen] {
1124
1124
  override def deriveWrapper[F[_, _], A, B](
1125
1125
  wrapped: Reflect[F, B],
1126
1126
  typeId: TypeId[A],
1127
- binding: Binding[BindingType.Wrapper[A, B], A],
1127
+ binding: Binding.Wrapper[A, B],
1128
1128
  doc: Doc,
1129
1129
  modifiers: Seq[Modifier.Reflect],
1130
1130
  defaultValue: Option[A],
@@ -1153,7 +1153,7 @@ The `derivePrimitive` method is responsible for deriving a `Gen` instance for pr
1153
1153
  def derivePrimitive[A](
1154
1154
  primitiveType: PrimitiveType[A],
1155
1155
  typeId: TypeId[A],
1156
- binding: Binding[BindingType.Primitive, A],
1156
+ binding: Binding.Primitive[A],
1157
1157
  doc: Doc,
1158
1158
  modifiers: Seq[Modifier.Reflect],
1159
1159
  defaultValue: Option[A],
@@ -1187,7 +1187,7 @@ The `deriveRecord` method is responsible for deriving a `Gen` instance for recor
1187
1187
  def deriveRecord[F[_, _], A](
1188
1188
  fields: IndexedSeq[Term[F, A, ?]],
1189
1189
  typeId: TypeId[A],
1190
- binding: Binding[BindingType.Record, A],
1190
+ binding: Binding.Record[A],
1191
1191
  doc: Doc,
1192
1192
  modifiers: Seq[Modifier.Reflect],
1193
1193
  defaultValue: Option[A],
@@ -1233,7 +1233,7 @@ The `deriveVariant` method is responsible for deriving a `Gen` instance for vari
1233
1233
  def deriveVariant[F[_, _], A](
1234
1234
  cases: IndexedSeq[Term[F, A, ?]],
1235
1235
  typeId: TypeId[A],
1236
- binding: Binding[BindingType.Variant, A],
1236
+ binding: Binding.Variant[A],
1237
1237
  doc: Doc,
1238
1238
  modifiers: Seq[Modifier.Reflect],
1239
1239
  defaultValue: Option[A],
@@ -1268,7 +1268,7 @@ The `deriveSequence` method is responsible for deriving a `Gen` instance for seq
1268
1268
  def deriveSequence[F[_, _], C[_], A](
1269
1269
  element: Reflect[F, A],
1270
1270
  typeId: TypeId[C[A]],
1271
- binding: Binding[BindingType.Seq[C], C[A]],
1271
+ binding: Binding.Seq[C, A],
1272
1272
  doc: Doc,
1273
1273
  modifiers: Seq[Modifier.Reflect],
1274
1274
  defaultValue: Option[C[A]],
@@ -1313,7 +1313,7 @@ def deriveMap[F[_, _], M[_, _], K, V](
1313
1313
  key: Reflect[F, K],
1314
1314
  value: Reflect[F, V],
1315
1315
  typeId: TypeId[M[K, V]],
1316
- binding: Binding[BindingType.Map[M], M[K, V]],
1316
+ binding: Binding.Map[M, K, V],
1317
1317
  doc: Doc,
1318
1318
  modifiers: Seq[Modifier.Reflect],
1319
1319
  defaultValue: Option[M[K, V]],
@@ -1352,7 +1352,7 @@ The `deriveDynamic` method is responsible for deriving a `Gen` instance for dyna
1352
1352
 
1353
1353
  ```scala
1354
1354
  def deriveDynamic[F[_, _]](
1355
- binding: Binding[BindingType.Dynamic, DynamicValue],
1355
+ binding: Binding.Dynamic,
1356
1356
  doc: Doc,
1357
1357
  modifiers: Seq[Modifier.Reflect],
1358
1358
  defaultValue: Option[DynamicValue],
@@ -1415,7 +1415,7 @@ The `deriveWrapper` method is responsible for deriving a `Gen` instance for wrap
1415
1415
  def deriveWrapper[F[_, _], A, B](
1416
1416
  wrapped: Reflect[F, B],
1417
1417
  typeId: TypeId[A],
1418
- binding: Binding[BindingType.Wrapper[A, B], A],
1418
+ binding: Binding.Wrapper[A, B],
1419
1419
  doc: Doc,
1420
1420
  modifiers: Seq[Modifier.Reflect],
1421
1421
  defaultValue: Option[A],
@@ -1456,7 +1456,7 @@ Now we can use the derived `Gen[Person]` instance to generate random `Person` va
1456
1456
 
1457
1457
  ```scala
1458
1458
  val random = new Random(42) // Seeded for reproducible output
1459
- // random: Random = scala.util.Random@5cfafbe0
1459
+ // random: Random = scala.util.Random@1d1d8934
1460
1460
 
1461
1461
  Person.gen.generate(random)
1462
1462
  // res14: Person = Person(name = "p", age = -1360544799)
package/ringbuffer.md ADDED
@@ -0,0 +1,249 @@
1
+ ---
2
+ id: ringbuffer
3
+ title: "Ring Buffer"
4
+ ---
5
+
6
+ # ZIO Blocks — Ring Buffer (`zio.blocks.ringbuffer`)
7
+
8
+ `zio.blocks.ringbuffer` is a family of **high-performance, bounded ring buffers** for the JVM (and Scala.js). Each variant is optimized for a specific producer/consumer threading pattern—pick the one that matches your use case and get the fastest possible inter-thread communication with zero dependencies.
9
+
10
+ All variants expose `offer` (returns `false` if full) and `take` (returns `null` if empty)—both non-blocking. Capacity must be a **power of two** (enables bitwise masking instead of modulo). Elements must be **non-null reference types** (`A <: AnyRef`).
11
+
12
+ ## Ring buffer variants
13
+
14
+ | Type | Producers | Consumers | Algorithm |
15
+ |------|-----------|-----------|-----------|
16
+ | `SpscRingBuffer` | 1 | 1 | FastFlow (null/non-null signaling) |
17
+ | `SpmcRingBuffer` | 1 | Many | Index-based capacity + CAS consumers |
18
+ | `MpscRingBuffer` | Many | 1 | CAS producers + relaxed poll |
19
+ | `MpmcRingBuffer` | Many | Many | Vyukov/Dmitry sequence buffer |
20
+
21
+ **Naming convention:** `S` = single, `M` = multi, `p` = producer, `c` = consumer.
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ ```scala
28
+ libraryDependencies += "dev.zio" %% "zio-blocks-ringbuffer" % "0.0.29"
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Quick start
34
+
35
+ ```scala
36
+ import zio.blocks.ringbuffer.SpscRingBuffer
37
+
38
+ val buf = SpscRingBuffer[String](1024) // capacity must be a power of 2
39
+
40
+ // Producer thread
41
+ buf.offer("hello") // true if inserted, false if full
42
+
43
+ // Consumer thread
44
+ val msg: String = buf.take() // element or null if empty
45
+ ```
46
+
47
+ ---
48
+
49
+ ## API
50
+
51
+ Every ring buffer provides:
52
+
53
+ ```scala
54
+ def offer(a: A): Boolean // insert; returns false if full
55
+ def take(): A // remove; returns null if empty
56
+ def size: Int // approximate element count
57
+ def isEmpty: Boolean // approximate emptiness check
58
+ def isFull: Boolean // approximate fullness check
59
+ ```
60
+
61
+ `SpscRingBuffer` additionally provides batch operations:
62
+
63
+ ```scala
64
+ def drain(consumer: A => Unit, limit: Int): Int // drain up to limit elements
65
+ def fill(supplier: () => A, limit: Int): Int // fill up to limit slots
66
+ ```
67
+
68
+ All `size`/`isEmpty`/`isFull` values are **approximate** under concurrency—they are snapshots that may be stale by the time the caller acts on them.
69
+
70
+ ---
71
+
72
+ ## Choosing a variant
73
+
74
+ Use the most constrained variant that fits your threading model:
75
+
76
+ | Scenario | Recommended |
77
+ |----------|-------------|
78
+ | Dedicated pipeline: one writer thread, one reader thread | `SpscRingBuffer` |
79
+ | Fan-in: many writers, one reader (e.g., logging, event aggregation) | `MpscRingBuffer` |
80
+ | Fan-out: one writer, many readers (e.g., work distribution) | `SpmcRingBuffer` |
81
+ | General purpose: any number of writers and readers | `MpmcRingBuffer` |
82
+
83
+ ---
84
+
85
+ ## Usage examples
86
+
87
+ ### SPSC with drain/fill
88
+
89
+ ```scala
90
+ import zio.blocks.ringbuffer.SpscRingBuffer
91
+
92
+ val buf = SpscRingBuffer[java.lang.Integer](64)
93
+
94
+ // Producer: batch-fill from a data source
95
+ var seq = 0
96
+ val filled = buf.fill(() => { seq += 1; Integer.valueOf(seq) }, 32)
97
+ println(s"Filled $filled elements")
98
+
99
+ // Consumer: batch-drain into a processor
100
+ val drained = buf.drain(e => println(s"Processing $e"), 32)
101
+ println(s"Drained $drained elements")
102
+ ```
103
+
104
+ ### MPSC fan-in (multiple producers, single consumer)
105
+
106
+ ```scala
107
+ import zio.blocks.ringbuffer.MpscRingBuffer
108
+
109
+ val buf = MpscRingBuffer[String](256)
110
+
111
+ // Multiple producer threads
112
+ for (i <- 0 until 4) {
113
+ new Thread(() => {
114
+ for (j <- 0 until 100)
115
+ buf.offer(s"producer-$i: message-$j")
116
+ }).start()
117
+ }
118
+
119
+ // Single consumer thread
120
+ new Thread(() => {
121
+ var msg = buf.take()
122
+ while (msg != null) {
123
+ println(msg)
124
+ msg = buf.take()
125
+ }
126
+ }).start()
127
+ ```
128
+
129
+ ### SPMC fan-out (single producer, multiple consumers)
130
+
131
+ ```scala
132
+ import zio.blocks.ringbuffer.SpmcRingBuffer
133
+
134
+ val buf = SpmcRingBuffer[String](256)
135
+
136
+ // Single producer thread
137
+ new Thread(() => {
138
+ for (i <- 0 until 400)
139
+ while (!buf.offer(s"task-$i")) {} // retry if full
140
+ }).start()
141
+
142
+ // Multiple consumer (worker) threads
143
+ for (w <- 0 until 4) {
144
+ new Thread(() => {
145
+ var msg = buf.take()
146
+ while (msg != null) {
147
+ println(s"worker-$w: $msg")
148
+ msg = buf.take()
149
+ }
150
+ }).start()
151
+ }
152
+ ```
153
+
154
+ ### Non-blocking try-once with fallback
155
+
156
+ ```scala
157
+ import zio.blocks.ringbuffer.MpmcRingBuffer
158
+
159
+ val buf = MpmcRingBuffer[String](64)
160
+
161
+ // Try to offer without blocking; handle backpressure yourself
162
+ if (!buf.offer("data")) {
163
+ // buffer is full — drop, log, or retry later
164
+ println("Buffer full, applying backpressure")
165
+ }
166
+
167
+ // Try to take without blocking
168
+ val result = buf.take()
169
+ if (result != null) {
170
+ println(s"Got: $result")
171
+ } else {
172
+ // buffer is empty — do other work
173
+ }
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Design notes
179
+
180
+ ### FastFlow pattern (SPSC)
181
+
182
+ `SpscRingBuffer` uses the FastFlow algorithm: the **null/non-null state of an array slot** is the synchronization signal. The producer never reads `consumerIndex`; the consumer never reads `producerIndex`. This minimizes cross-core cache traffic to a single cache line per operation.
183
+
184
+ A **look-ahead step** (`capacity/4`, capped at 4096) lets the producer batch-check multiple future slots at once, further reducing the frequency of slow-path reads.
185
+
186
+ ### Vyukov/Dmitry sequence buffer (MPMC)
187
+
188
+ `MpmcRingBuffer` uses a parallel `Long` sequence buffer alongside the data array. Each slot carries a stamp indicating whether it is available for writing or reading. Both `producerIndex` and `consumerIndex` are advanced via CAS, allowing any number of threads on both sides. The minimum capacity is 2 (the algorithm requires at least 2 slots to distinguish written from consumed).
189
+
190
+ ### CAS-based producers (MPSC)
191
+
192
+ `MpscRingBuffer` follows the JCTools `MpscArrayQueue` design: producers claim a slot via CAS on `producerIndex`, then write the element with release semantics. A cached `producerLimit` avoids reading `consumerIndex` on every offer, reducing cross-core traffic. The consumer side uses relaxed poll semantics—a `null` slot means either empty or a producer mid-write.
193
+
194
+ ### Index-based SPMC
195
+
196
+ `SpmcRingBuffer` uses index-based capacity checking on the producer side (no CAS needed for a single producer). Consumers use a CAS loop on `consumerIndex`. Consumers read the element *before* the CAS to avoid a race with the producer overwriting the slot. Consumers do not null array slots after reading—the producer overwrites them on the next lap.
197
+
198
+ ### Cache-line padding
199
+
200
+ All ring buffers use **128-byte padding regions** (16 `Long` fields) between producer and consumer fields. This prevents false sharing on all architectures, including Apple Silicon which uses 128-byte cache lines (most x86 CPUs use 64-byte lines).
201
+
202
+ The padding is implemented via a class hierarchy:
203
+
204
+ ```
205
+ Pad0 → ProducerFields → Pad1 → ConsumerFields → Pad2
206
+ ```
207
+
208
+ ### VarHandle for memory ordering
209
+
210
+ All JVM implementations use `java.lang.invoke.VarHandle` (Java 9+) for acquire/release semantics instead of `sun.misc.Unsafe`. This is the recommended modern approach for lock-free data structures on the JVM.
211
+
212
+ ### Power-of-two masking
213
+
214
+ Capacity must be a power of two. This allows `index & (capacity - 1)` instead of `index % capacity`, which is significantly faster because bitwise AND compiles to a single CPU instruction.
215
+
216
+ ---
217
+
218
+ ## Thread-safety contract
219
+
220
+ Violating the threading contract (e.g., calling `take` from multiple threads on an `SpscRingBuffer`) results in **undefined behavior**. No runtime check is performed—this is enforced by contract for maximum performance.
221
+
222
+ | Type | `offer` | `take` |
223
+ |------|---------|--------|
224
+ | `SpscRingBuffer` | Single producer thread only | Single consumer thread only |
225
+ | `SpmcRingBuffer` | Single producer thread only | Any number of consumer threads |
226
+ | `MpscRingBuffer` | Any number of producer threads | Single consumer thread only |
227
+ | `MpmcRingBuffer` | Any number of producer threads | Any number of consumer threads |
228
+
229
+ ---
230
+
231
+ ## Performance characteristics
232
+
233
+ | Operation | Complexity |
234
+ |-----------|-----------|
235
+ | `offer` | Lock-free (SPSC/SPMC: wait-free) |
236
+ | `take` | Lock-free (SPSC/MPSC: wait-free) |
237
+
238
+ **SPSC** is the fastest: no CAS, no locks, minimal cache-line traffic. Use it whenever your threading model allows a dedicated producer-consumer pair.
239
+
240
+ ---
241
+
242
+ ## Cross-platform support
243
+
244
+ On **Scala.js**, all ring buffer types compile and provide the same API surface. Since Scala.js is single-threaded, the JS implementations use plain reads and writes with no memory ordering primitives.
245
+
246
+ | Platform | Support |
247
+ |----------|---------|
248
+ | JVM | Full concurrency support |
249
+ | Scala.js | Sequential (same API) |
package/sidebars.js CHANGED
@@ -13,7 +13,6 @@ const sidebars = {
13
13
  "reference/binding-resolver",
14
14
  "reference/registers",
15
15
  "reference/typeid",
16
- "reference/allows",
17
16
  "reference/modifier",
18
17
  "reference/dynamic-value",
19
18
  "reference/dynamic-schema",
@@ -40,14 +39,27 @@ const sidebars = {
40
39
  ]
41
40
  },
42
41
  "reference/context",
43
- "scope",
42
+ {
43
+ type: "category",
44
+ label: "Resource Management & DI",
45
+ link: { type: "doc", id: "reference/resource-management-di/index" },
46
+ items: [
47
+ "reference/resource-management-di/resource",
48
+ "reference/resource-management-di/scope",
49
+ "reference/resource-management-di/wire",
50
+ ]
51
+ },
52
+ "reference/combinators",
44
53
  "reference/docs",
45
54
  "reference/json",
46
55
  "reference/json-patch",
56
+ "reference/json-differ",
47
57
  "reference/json-schema",
48
58
  "reference/xml",
49
59
  "reference/syntax",
50
60
  "reference/media-type",
61
+ "reference/http-model",
62
+ "ringbuffer",
51
63
  ]
52
64
  },
53
65
  {