@@ -42,6 +42,9 @@ public class Buffer : Source, Sink {
42
42
@JvmField
43
43
internal var head: Segment ? = null
44
44
45
+ @JvmField
46
+ internal var tail: Segment ? = null
47
+
45
48
/* *
46
49
* The number of bytes accessible for read from this buffer.
47
50
*/
@@ -76,8 +79,7 @@ public class Buffer : Source, Sink {
76
79
val b = data[pos++ ]
77
80
size - = 1L
78
81
if (pos == limit) {
79
- head = segment.pop()
80
- SegmentPool .recycle(segment)
82
+ recycleHead()
81
83
} else {
82
84
segment.pos = pos
83
85
}
@@ -102,8 +104,7 @@ public class Buffer : Source, Sink {
102
104
size - = 2L
103
105
104
106
if (pos == limit) {
105
- head = segment.pop()
106
- SegmentPool .recycle(segment)
107
+ recycleHead()
107
108
} else {
108
109
segment.pos = pos
109
110
}
@@ -138,8 +139,7 @@ public class Buffer : Source, Sink {
138
139
size - = 4L
139
140
140
141
if (pos == limit) {
141
- head = segment.pop()
142
- SegmentPool .recycle(segment)
142
+ recycleHead()
143
143
} else {
144
144
segment.pos = pos
145
145
}
@@ -176,8 +176,7 @@ public class Buffer : Source, Sink {
176
176
size - = 8L
177
177
178
178
if (pos == limit) {
179
- head = segment.pop()
180
- SegmentPool .recycle(segment)
179
+ recycleHead()
181
180
} else {
182
181
segment.pos = pos
183
182
}
@@ -241,13 +240,7 @@ public class Buffer : Source, Sink {
241
240
val copy = s!! .sharedCopy()
242
241
copy.pos + = currentOffset.toInt()
243
242
copy.limit = minOf(copy.pos + remainingByteCount.toInt(), copy.limit)
244
- if (out .head == null ) {
245
- copy.prev = copy
246
- copy.next = copy.prev
247
- out .head = copy.next
248
- } else {
249
- out .head!! .prev!! .push(copy)
250
- }
243
+ out .pushSegment(copy)
251
244
remainingByteCount - = (copy.limit - copy.pos).toLong()
252
245
currentOffset = 0L
253
246
s = s.next
@@ -264,7 +257,7 @@ public class Buffer : Source, Sink {
264
257
if (result == 0L ) return 0L
265
258
266
259
// Omit the tail if it's still writable.
267
- val tail = head !! .prev !!
260
+ val tail = tail !!
268
261
if (tail.limit < Segment .SIZE && tail.owner) {
269
262
result - = (tail.limit - tail.pos).toLong()
270
263
}
@@ -317,8 +310,7 @@ public class Buffer : Source, Sink {
317
310
head.pos + = toSkip
318
311
319
312
if (head.pos == head.limit) {
320
- this .head = head.pop()
321
- SegmentPool .recycle(head)
313
+ recycleHead()
322
314
}
323
315
}
324
316
}
@@ -336,8 +328,7 @@ public class Buffer : Source, Sink {
336
328
size - = toCopy.toLong()
337
329
338
330
if (s.pos == s.limit) {
339
- head = s.pop()
340
- SegmentPool .recycle(s)
331
+ recycleHead()
341
332
}
342
333
343
334
return toCopy
@@ -377,19 +368,20 @@ public class Buffer : Source, Sink {
377
368
internal fun writableSegment (minimumCapacity : Int ): Segment {
378
369
require(minimumCapacity >= 1 && minimumCapacity <= Segment .SIZE ) { " unexpected capacity" }
379
370
380
- if (head == null ) {
371
+ if (tail == null ) {
381
372
val result = SegmentPool .take() // Acquire a first segment.
382
373
head = result
383
- result.prev = result
384
- result.next = result
374
+ tail = result
385
375
return result
386
376
}
387
377
388
- var tail = head!! .prev
389
- if (tail!! .limit + minimumCapacity > Segment .SIZE || ! tail.owner) {
390
- tail = tail.push(SegmentPool .take()) // Append a new empty segment to fill up.
378
+ val t = tail!!
379
+ if (t.limit + minimumCapacity > Segment .SIZE || ! t.owner) {
380
+ val newTail = t.push(SegmentPool .take()) // Append a new empty segment to fill up.
381
+ tail = newTail
382
+ return newTail
391
383
}
392
- return tail
384
+ return t
393
385
}
394
386
395
387
override fun write (source : ByteArray , startIndex : Int , endIndex : Int ) {
@@ -486,7 +478,7 @@ public class Buffer : Source, Sink {
486
478
while (remainingByteCount > 0L ) {
487
479
// Is a prefix of the source's head segment all that we need to move?
488
480
if (remainingByteCount < source.head!! .limit - source.head!! .pos) {
489
- val tail = if (head != null ) head !! .prev else null
481
+ val tail = tail
490
482
if (tail != null && tail.owner &&
491
483
remainingByteCount + tail.limit - (if (tail.shared) 0 else tail.pos) <= Segment .SIZE
492
484
) {
@@ -498,23 +490,22 @@ public class Buffer : Source, Sink {
498
490
} else {
499
491
// We're going to need another segment. Split the source's head
500
492
// segment in two, then move the first of those two to this buffer.
501
- source.head = source.head!! .split(remainingByteCount.toInt())
493
+ val newHead = source.head!! .split(remainingByteCount.toInt())
494
+ if (source.head == source.tail) {
495
+ source.tail = newHead
496
+ }
497
+ source.head = newHead
502
498
}
503
499
}
504
500
505
501
// Remove the source's head segment and append it to our tail.
506
502
val segmentToMove = source.head
507
503
val movedByteCount = (segmentToMove!! .limit - segmentToMove.pos).toLong()
508
504
source.head = segmentToMove.pop()
509
- if (head == null ) {
510
- head = segmentToMove
511
- segmentToMove.prev = segmentToMove
512
- segmentToMove.next = segmentToMove.prev
513
- } else {
514
- var tail = head!! .prev
515
- tail = tail!! .push(segmentToMove)
516
- tail.compact()
505
+ if (source.head == null ) {
506
+ source.tail = null
517
507
}
508
+ pushSegment(segmentToMove, true )
518
509
source.size - = movedByteCount
519
510
size + = movedByteCount
520
511
remainingByteCount - = movedByteCount
@@ -582,16 +573,15 @@ public class Buffer : Source, Sink {
582
573
val result = Buffer ()
583
574
if (size == 0L ) return result
584
575
585
- val head = head!!
576
+ val head = this . head!!
586
577
val headCopy = head.sharedCopy()
587
578
588
579
result.head = headCopy
589
- headCopy.prev = result.head
590
- headCopy.next = headCopy.prev
580
+ result.tail = headCopy
591
581
592
582
var s = head.next
593
- while (s != = head ) {
594
- headCopy.prev !! .push(s!! .sharedCopy())
583
+ while (s != null ) {
584
+ result.tail = result.tail !! .push(s.sharedCopy())
595
585
s = s.next
596
586
}
597
587
@@ -642,6 +632,63 @@ public class Buffer : Source, Sink {
642
632
643
633
return " Buffer(size=$size hex=$builder )"
644
634
}
635
+
636
+ /* *
637
+ * Unlinks and recycles this buffer's head.
638
+ *
639
+ * If head had a successor, it'll become a new head.
640
+ * Otherwise, both [head] and [tail] will be set to null.
641
+ *
642
+ * It's up to a caller to ensure that the head exists.
643
+ */
644
+ internal fun recycleHead () {
645
+ val oldHead = head!!
646
+ val nextHead = oldHead.next
647
+ head = nextHead
648
+ if (nextHead == null ) {
649
+ tail = null
650
+ } else {
651
+ nextHead.prev = null
652
+ }
653
+ oldHead.next = null
654
+ SegmentPool .recycle(oldHead)
655
+ }
656
+
657
+ /* *
658
+ * Unlinks and recycles this buffer's tail segment.
659
+ *
660
+ * If tail had a predecessor, it'll become a new tail.
661
+ * Otherwise, both [head] and [tail] will be set to null.
662
+ *
663
+ * It's up to a caller to ensure that the tail exists.
664
+ */
665
+ internal fun recycleTail () {
666
+ val oldTail = tail!!
667
+ val newTail = oldTail.prev
668
+ tail = newTail
669
+ if (newTail == null ) {
670
+ head = null
671
+ } else {
672
+ newTail.next = null
673
+ }
674
+ oldTail.prev = null
675
+ SegmentPool .recycle(oldTail)
676
+ }
677
+
678
+ @Suppress(" NOTHING_TO_INLINE" )
679
+ private inline fun pushSegment (newTail : Segment , tryCompact : Boolean = false) {
680
+ if (head == null ) {
681
+ head = newTail
682
+ tail = newTail
683
+ } else if (tryCompact) {
684
+ tail = tail!! .push(newTail).compact()
685
+ if (tail!! .prev == null ) {
686
+ head = tail
687
+ }
688
+ } else {
689
+ tail = tail!! .push(newTail)
690
+ }
691
+ }
645
692
}
646
693
647
694
/* *
@@ -652,23 +699,26 @@ internal inline fun <T> Buffer.seek(
652
699
fromIndex : Long ,
653
700
lambda : (Segment ? , Long ) -> T
654
701
): T {
655
- var s : Segment = head ? : return lambda(null , - 1L )
702
+ if ( this .head == null ) lambda(null , - 1L )
656
703
657
704
if (size - fromIndex < fromIndex) {
705
+ var s = tail
658
706
// We're scanning in the back half of this buffer. Find the segment starting at the back.
659
707
var offset = size
660
- while (offset > fromIndex) {
661
- s = s.prev!!
708
+ while (s != null && offset > fromIndex) {
662
709
offset - = (s.limit - s.pos).toLong()
710
+ if (offset <= fromIndex) break
711
+ s = s.prev
663
712
}
664
713
return lambda(s, offset)
665
714
} else {
715
+ var s = this .head
666
716
// We're scanning in the front half of this buffer. Find the segment starting at the front.
667
717
var offset = 0L
668
- while (true ) {
718
+ while (s != null ) {
669
719
val nextOffset = offset + (s.limit - s.pos)
670
720
if (nextOffset > fromIndex) break
671
- s = s.next!!
721
+ s = s.next
672
722
offset = nextOffset
673
723
}
674
724
return lambda(s, offset)
0 commit comments