1
1
/*
2
- * Copyright 2002-2022 the original author or authors.
2
+ * Copyright 2002-2023 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .core .io .buffer ;
18
18
19
+ import java .io .Closeable ;
19
20
import java .io .InputStream ;
20
21
import java .io .OutputStream ;
22
+ import java .io .UncheckedIOException ;
21
23
import java .nio .ByteBuffer ;
22
24
import java .nio .CharBuffer ;
25
+ import java .nio .charset .CharacterCodingException ;
23
26
import java .nio .charset .Charset ;
24
27
import java .nio .charset .CharsetEncoder ;
25
28
import java .nio .charset .CoderResult ;
26
29
import java .nio .charset .CodingErrorAction ;
30
+ import java .util .Iterator ;
27
31
import java .util .function .IntPredicate ;
28
32
29
33
import org .springframework .util .Assert ;
@@ -265,27 +269,28 @@ default DataBuffer write(CharSequence charSequence, Charset charset) {
265
269
CharsetEncoder charsetEncoder = charset .newEncoder ()
266
270
.onMalformedInput (CodingErrorAction .REPLACE )
267
271
.onUnmappableCharacter (CodingErrorAction .REPLACE );
268
- CharBuffer inBuffer = CharBuffer .wrap (charSequence );
269
- int estimatedSize = (int ) (inBuffer .remaining () * charsetEncoder .averageBytesPerChar ());
270
- ByteBuffer outBuffer = ensureCapacity (estimatedSize )
271
- .asByteBuffer (writePosition (), writableByteCount ());
272
- while (true ) {
273
- CoderResult cr = (inBuffer .hasRemaining () ?
274
- charsetEncoder .encode (inBuffer , outBuffer , true ) : CoderResult .UNDERFLOW );
275
- if (cr .isUnderflow ()) {
276
- cr = charsetEncoder .flush (outBuffer );
272
+ CharBuffer src = CharBuffer .wrap (charSequence );
273
+ int length = (int ) (src .remaining () * charsetEncoder .maxBytesPerChar ());
274
+ ensureWritable (length );
275
+ try (ByteBufferIterator iterator = writableByteBuffers ()) {
276
+ Assert .state (iterator .hasNext (), "No ByteBuffer available" );
277
+ ByteBuffer dest = iterator .next ();
278
+ int pos = dest .position ();
279
+ CoderResult cr = charsetEncoder .encode (src , dest , true );
280
+ if (!cr .isUnderflow ()) {
281
+ cr .throwException ();
277
282
}
278
- if (cr .isUnderflow ()) {
279
- break ;
280
- }
281
- if (cr .isOverflow ()) {
282
- writePosition (writePosition () + outBuffer .position ());
283
- int maximumSize = (int ) (inBuffer .remaining () * charsetEncoder .maxBytesPerChar ());
284
- ensureCapacity (maximumSize );
285
- outBuffer = asByteBuffer (writePosition (), writableByteCount ());
283
+ cr = charsetEncoder .flush (dest );
284
+ if (!cr .isUnderflow ()) {
285
+ cr .throwException ();
286
286
}
287
+ length = dest .position () - pos ;
288
+ }
289
+ catch (CharacterCodingException ex ) {
290
+ // should not happen, because the encoder uses action REPLACE
291
+ throw new UncheckedIOException (ex );
287
292
}
288
- writePosition (writePosition () + outBuffer . position () );
293
+ writePosition (writePosition () + length );
289
294
}
290
295
return this ;
291
296
}
@@ -353,8 +358,8 @@ default DataBuffer retainedSlice(int index, int length) {
353
358
* changes in the returned buffer's {@linkplain ByteBuffer#position() position}
354
359
* will not be reflected in the reading nor writing position of this data buffer.
355
360
* @return this data buffer as a byte buffer
356
- * @deprecated as of 6.0, in favor of {@link #toByteBuffer()}, which does
357
- * <strong>not</strong> share data and returns a copy .
361
+ * @deprecated as of 6.0, in favor of {@link #toByteBuffer(ByteBuffer )},
362
+ * {@link #readableByteBuffers()}, or {@link #writableByteBuffers()} .
358
363
*/
359
364
@ Deprecated (since = "6.0" )
360
365
ByteBuffer asByteBuffer ();
@@ -368,8 +373,8 @@ default DataBuffer retainedSlice(int index, int length) {
368
373
* @param length the length of the returned byte buffer
369
374
* @return this data buffer as a byte buffer
370
375
* @since 5.0.1
371
- * @deprecated as of 6.0, in favor of {@link #toByteBuffer(int, int)}, which
372
- * does <strong>not</strong> share data and returns a copy .
376
+ * @deprecated as of 6.0, in favor of {@link #toByteBuffer(int, ByteBuffer, int, int )},
377
+ * {@link #readableByteBuffers()}, or {@link #writableByteBuffers()} .
373
378
*/
374
379
@ Deprecated (since = "6.0" )
375
380
ByteBuffer asByteBuffer (int index , int length );
@@ -380,7 +385,11 @@ default DataBuffer retainedSlice(int index, int length) {
380
385
* <strong>not</strong> shared.
381
386
* @return this data buffer as a byte buffer
382
387
* @since 6.0
388
+ * @see #readableByteBuffers()
389
+ * @see #writableByteBuffers()
390
+ * @deprecated as of 6.0.5, in favor of {@link #toByteBuffer(ByteBuffer)}
383
391
*/
392
+ @ Deprecated (since = "6.0.5" )
384
393
default ByteBuffer toByteBuffer () {
385
394
return toByteBuffer (readPosition (), readableByteCount ());
386
395
}
@@ -391,9 +400,67 @@ default ByteBuffer toByteBuffer() {
391
400
* {@code ByteBuffer} is <strong>not</strong> shared.
392
401
* @return this data buffer as a byte buffer
393
402
* @since 6.0
403
+ * @see #readableByteBuffers()
404
+ * @see #writableByteBuffers()
405
+ * @deprecated as of 6.0.5, in favor of
406
+ * {@link #toByteBuffer(int, ByteBuffer, int, int)}
394
407
*/
408
+ @ Deprecated (since = "6.0.5" )
395
409
ByteBuffer toByteBuffer (int index , int length );
396
410
411
+ /**
412
+ * Copies this entire data buffer into the given destination
413
+ * {@code ByteBuffer}, beginning at the current
414
+ * {@linkplain #readPosition() reading position}, and the current
415
+ * {@linkplain ByteBuffer#position() position} of destination byte buffer.
416
+ * @param dest the destination byte buffer
417
+ * @since 6.0.5
418
+ */
419
+ default void toByteBuffer (ByteBuffer dest ) {
420
+ toByteBuffer (readPosition (), dest , dest .position (), readableByteCount ());
421
+ }
422
+
423
+ /**
424
+ * Copies the given length from this data buffer into the given destination
425
+ * {@code ByteBuffer}, beginning at the given source position, and the
426
+ * given destination position in the destination byte buffer.
427
+ * @param srcPos the position of this data buffer from where copying should
428
+ * start
429
+ * @param dest the destination byte buffer
430
+ * @param destPos the position in {@code dest} to where copying should
431
+ * start
432
+ * @param length the amount of data to copy
433
+ * @since 6.0.5
434
+ */
435
+ void toByteBuffer (int srcPos , ByteBuffer dest , int destPos , int length );
436
+
437
+ /**
438
+ * Returns a closeable iterator over each {@link ByteBuffer} in this data
439
+ * buffer that can be read. Calling this method is more efficient than
440
+ * {@link #toByteBuffer()}, as no data is copied. However, the byte buffers
441
+ * provided can only be used during the iteration.
442
+ *
443
+ * <p><b>Note</b> that the returned iterator must be used in a
444
+ * try-with-resources clause or explicitly
445
+ * {@linkplain ByteBufferIterator#close() closed}.
446
+ * @return a closeable iterator over the readable byte buffers contained in this data buffer
447
+ * @since 6.0.5
448
+ */
449
+ ByteBufferIterator readableByteBuffers ();
450
+
451
+ /**
452
+ * Returns a closeable iterator over each {@link ByteBuffer} in this data
453
+ * buffer that can be written to. The byte buffers provided can only be used
454
+ * during the iteration.
455
+ *
456
+ * <p><b>Note</b> that the returned iterator must be used in a
457
+ * try-with-resources clause or explicitly
458
+ * {@linkplain ByteBufferIterator#close() closed}.
459
+ * @return a closeable iterator over the writable byte buffers contained in this data buffer
460
+ * @since 6.0.5
461
+ */
462
+ ByteBufferIterator writableByteBuffers ();
463
+
397
464
/**
398
465
* Expose this buffer's data as an {@link InputStream}. Both data and read position are
399
466
* shared between the returned stream and this data buffer. The underlying buffer will
@@ -450,4 +517,20 @@ default String toString(Charset charset) {
450
517
*/
451
518
String toString (int index , int length , Charset charset );
452
519
520
+
521
+ /**
522
+ * A dedicated iterator type that ensures the lifecycle of iterated
523
+ * {@link ByteBuffer} elements. This iterator must be used in a
524
+ * try-with-resources clause or explicitly {@linkplain #close() closed}.
525
+ *
526
+ * @see DataBuffer#readableByteBuffers()
527
+ * @see DataBuffer#writableByteBuffers()
528
+ */
529
+ interface ByteBufferIterator extends Iterator <ByteBuffer >, Closeable {
530
+
531
+ @ Override
532
+ void close ();
533
+
534
+ }
535
+
453
536
}
0 commit comments