@@ -54,68 +54,102 @@ final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
5454 private boolean closed ;
5555 private boolean readOnly ;
5656
57- CompositeBuf (Allocator allocator , Buf [] bufs ) {
58- this (allocator , true , filterExternalBufs (bufs ), COMPOSITE_DROP );
57+ CompositeBuf (Allocator allocator , Deref < Buf > [] refs ) {
58+ this (allocator , true , filterExternalBufs (refs ), COMPOSITE_DROP , false );
5959 }
6060
61- private static Buf [] filterExternalBufs (Buf [] bufs ) {
61+ private static Buf [] filterExternalBufs (Deref < Buf > [] refs ) {
6262 // We filter out all zero-capacity buffers because they wouldn't contribute to the composite buffer anyway,
6363 // and also, by ensuring that all constituent buffers contribute to the size of the composite buffer,
6464 // we make sure that the number of composite buffers will never become greater than the number of bytes in
6565 // the composite buffer.
6666 // This restriction guarantees that methods like countComponents, forEachReadable and forEachWritable,
6767 // will never overflow their component counts.
6868 // Allocating a new array unconditionally also prevents external modification of the array.
69- bufs = Arrays .stream (bufs )
70- .filter (b -> b .capacity () > 0 )
69+ Buf [] bufs = Arrays .stream (refs )
70+ .map (r -> r .get ()) // Increments reference counts.
71+ .filter (CompositeBuf ::discardEmpty )
7172 .flatMap (CompositeBuf ::flattenBuffer )
7273 .toArray (Buf []::new );
7374 // Make sure there are no duplicates among the buffers.
7475 Set <Buf > duplicatesCheck = Collections .newSetFromMap (new IdentityHashMap <>());
7576 duplicatesCheck .addAll (Arrays .asList (bufs ));
7677 if (duplicatesCheck .size () < bufs .length ) {
78+ for (Buf buf : bufs ) {
79+ buf .close (); // Undo the increment we did with Deref.get().
80+ }
7781 throw new IllegalArgumentException (
7882 "Cannot create composite buffer with duplicate constituent buffer components." );
7983 }
8084 return bufs ;
8185 }
8286
87+ private static boolean discardEmpty (Buf buf ) {
88+ if (buf .capacity () > 0 ) {
89+ return true ;
90+ } else {
91+ // If we filter a buffer out, then we must make sure to close it since we incremented the reference count
92+ // with Deref.get() earlier.
93+ buf .close ();
94+ return false ;
95+ }
96+ }
97+
8398 private static Stream <Buf > flattenBuffer (Buf buf ) {
8499 if (buf instanceof CompositeBuf ) {
85- return Stream .of (((CompositeBuf ) buf ).bufs );
100+ // Extract components and move our reference count from the composite onto the components.
101+ var composite = (CompositeBuf ) buf ;
102+ var bufs = composite .bufs ;
103+ for (Buf b : bufs ) {
104+ b .acquire ();
105+ }
106+ buf .close (); // Important: acquire on components *before* closing composite.
107+ return Stream .of (bufs );
86108 }
87109 return Stream .of (buf );
88110 }
89111
90- private CompositeBuf (Allocator allocator , boolean isSendable , Buf [] bufs , Drop <CompositeBuf > drop ) {
112+ private CompositeBuf (Allocator allocator , boolean isSendable , Buf [] bufs , Drop <CompositeBuf > drop ,
113+ boolean acquireBufs ) {
91114 super (drop );
92115 this .allocator = allocator ;
93116 this .isSendable = isSendable ;
94- for (Buf buf : bufs ) {
95- buf .acquire ();
96- }
97- if (bufs .length > 0 ) {
98- ByteOrder targetOrder = bufs [0 ].order ();
117+ if (acquireBufs ) {
99118 for (Buf buf : bufs ) {
100- if (buf .order () != targetOrder ) {
101- throw new IllegalArgumentException ("Constituent buffers have inconsistent byte order." );
102- }
119+ buf .acquire ();
103120 }
104- order = bufs [0 ].order ();
121+ }
122+ try {
123+ if (bufs .length > 0 ) {
124+ ByteOrder targetOrder = bufs [0 ].order ();
125+ for (Buf buf : bufs ) {
126+ if (buf .order () != targetOrder ) {
127+ throw new IllegalArgumentException ("Constituent buffers have inconsistent byte order." );
128+ }
129+ }
130+ order = bufs [0 ].order ();
105131
106- boolean targetReadOnly = bufs [0 ].readOnly ();
107- for (Buf buf : bufs ) {
108- if (buf .readOnly () != targetReadOnly ) {
109- throw new IllegalArgumentException ("Constituent buffers have inconsistent read-only state." );
132+ boolean targetReadOnly = bufs [0 ].readOnly ();
133+ for (Buf buf : bufs ) {
134+ if (buf .readOnly () != targetReadOnly ) {
135+ throw new IllegalArgumentException ("Constituent buffers have inconsistent read-only state." );
136+ }
110137 }
138+ readOnly = targetReadOnly ;
139+ } else {
140+ order = ByteOrder .nativeOrder ();
111141 }
112- readOnly = targetReadOnly ;
113- } else {
114- order = ByteOrder .nativeOrder ();
142+ this .bufs = bufs ;
143+ computeBufferOffsets ();
144+ tornBufAccessors = new TornBufAccessors (this );
145+ } catch (Exception e ) {
146+ // Always close bufs on exception, regardless of acquireBufs value.
147+ // If acquireBufs is false, it just means the ref count increments happened prior to this constructor call.
148+ for (Buf buf : bufs ) {
149+ buf .close ();
150+ }
151+ throw e ;
115152 }
116- this .bufs = bufs ;
117- computeBufferOffsets ();
118- tornBufAccessors = new TornBufAccessors (this );
119153 }
120154
121155 private void computeBufferOffsets () {
@@ -292,7 +326,7 @@ public Buf slice(int offset, int length) {
292326 slices = new Buf [] { choice .slice (subOffset , 0 ) };
293327 }
294328
295- return new CompositeBuf (allocator , false , slices , drop );
329+ return new CompositeBuf (allocator , false , slices , drop , true );
296330 } catch (Throwable throwable ) {
297331 // We called acquire prior to the try-clause. We need to undo that if we're not creating a composite buffer:
298332 close ();
@@ -718,7 +752,7 @@ public Buf bifurcate() {
718752 }
719753 if (bufs .length == 0 ) {
720754 // Bifurcating a zero-length buffer is trivial.
721- return new CompositeBuf (allocator , true , bufs , unsafeGetDrop ()).order (order );
755+ return new CompositeBuf (allocator , true , bufs , unsafeGetDrop (), true ).order (order );
722756 }
723757
724758 int i = searchOffsets (woff );
@@ -730,7 +764,7 @@ public Buf bifurcate() {
730764 }
731765 computeBufferOffsets ();
732766 try {
733- var compositeBuf = new CompositeBuf (allocator , true , bifs , unsafeGetDrop ());
767+ var compositeBuf = new CompositeBuf (allocator , true , bifs , unsafeGetDrop (), true );
734768 compositeBuf .order = order ; // Preserve byte order even if bifs array is empty.
735769 return compositeBuf ;
736770 } finally {
@@ -1133,7 +1167,7 @@ public CompositeBuf transferOwnership(Drop<CompositeBuf> drop) {
11331167 for (int i = 0 ; i < sends .length ; i ++) {
11341168 received [i ] = sends [i ].receive ();
11351169 }
1136- var composite = new CompositeBuf (allocator , true , received , drop );
1170+ var composite = new CompositeBuf (allocator , true , received , drop , true );
11371171 composite .readOnly = readOnly ;
11381172 drop .attach (composite );
11391173 return composite ;
0 commit comments