0
# Buffer Management
1
2
MINA Core provides the `IoBuffer` class as an enhanced replacement for Java NIO's `ByteBuffer`. IoBuffer offers automatic expansion, convenient data manipulation methods, and optimized memory management for network applications.
3
4
## IoBuffer Basics
5
6
### Core Interface
7
8
```java { .api }
9
public abstract class IoBuffer implements Comparable<IoBuffer> {
10
// Allocation methods
11
static IoBuffer allocate(int capacity);
12
static IoBuffer allocate(int capacity, boolean direct);
13
static IoBuffer wrap(byte[] array);
14
static IoBuffer wrap(ByteBuffer nioBuffer);
15
16
// Buffer properties
17
int capacity();
18
int position();
19
IoBuffer position(int newPosition);
20
int limit();
21
IoBuffer limit(int newLimit);
22
int remaining();
23
boolean hasRemaining();
24
25
// Auto-expansion
26
boolean isAutoExpand();
27
IoBuffer setAutoExpand(boolean autoExpand);
28
boolean isAutoShrink();
29
IoBuffer setAutoShrink(boolean autoShrink);
30
31
// Buffer manipulation
32
IoBuffer clear();
33
IoBuffer flip();
34
IoBuffer rewind();
35
IoBuffer mark();
36
IoBuffer reset();
37
IoBuffer compact();
38
39
// Data access
40
byte get();
41
IoBuffer put(byte b);
42
byte get(int index);
43
IoBuffer put(int index, byte b);
44
45
// Bulk operations
46
IoBuffer get(byte[] dst);
47
IoBuffer put(byte[] src);
48
IoBuffer get(byte[] dst, int offset, int length);
49
IoBuffer put(byte[] src, int offset, int length);
50
51
// String operations
52
String getString(CharsetDecoder decoder) throws CharacterCodingException;
53
String getString(int fieldSize, CharsetDecoder decoder) throws CharacterCodingException;
54
IoBuffer putString(String value, CharsetEncoder encoder) throws CharacterCodingException;
55
String getHexDump();
56
String getHexDump(int lengthLimit);
57
}
58
```
59
60
## Buffer Allocation
61
62
### Basic Allocation
63
64
```java { .api }
65
// Allocate heap buffer
66
IoBuffer buffer = IoBuffer.allocate(1024);
67
68
// Allocate direct buffer (off-heap)
69
IoBuffer directBuffer = IoBuffer.allocate(1024, true);
70
71
// Set default buffer type
72
IoBuffer.setUseDirectBuffer(true); // Use direct buffers by default
73
IoBuffer defaultBuffer = IoBuffer.allocate(1024); // Now allocates direct buffer
74
75
// Check if buffer is direct
76
if (buffer.isDirect()) {
77
System.out.println("Using direct buffer");
78
} else {
79
System.out.println("Using heap buffer");
80
}
81
```
82
83
### Buffer Wrapping
84
85
```java { .api }
86
// Wrap byte array
87
byte[] data = "Hello, World!".getBytes("UTF-8");
88
IoBuffer wrapped = IoBuffer.wrap(data);
89
90
// Wrap NIO ByteBuffer
91
ByteBuffer nioBuffer = ByteBuffer.allocate(1024);
92
IoBuffer fromNio = IoBuffer.wrap(nioBuffer);
93
94
// Wrap with automatic expansion
95
IoBuffer autoExpanding = IoBuffer.wrap(data);
96
autoExpanding.setAutoExpand(true);
97
98
// Create buffer from existing buffer
99
IoBuffer original = IoBuffer.allocate(100);
100
IoBuffer duplicate = original.duplicate();
101
IoBuffer slice = original.slice();
102
IoBuffer readOnlyView = original.asReadOnlyBuffer();
103
```
104
105
## Auto-Expansion and Auto-Shrinking
106
107
### Auto-Expansion
108
109
```java { .api }
110
// Enable auto-expansion to handle variable-length data
111
IoBuffer buffer = IoBuffer.allocate(16);
112
buffer.setAutoExpand(true);
113
114
// Write data without worrying about capacity
115
String longMessage = "This is a very long message that exceeds the initial buffer capacity";
116
buffer.putString(longMessage, Charset.forName("UTF-8").newEncoder());
117
118
System.out.println("Buffer capacity grew to: " + buffer.capacity());
119
120
// Auto-expansion in action
121
public class AutoExpandExample {
122
123
public void writeVariableLengthData(IoSession session, List<String> messages) {
124
IoBuffer buffer = IoBuffer.allocate(64);
125
buffer.setAutoExpand(true);
126
127
// Write message count
128
buffer.putInt(messages.size());
129
130
// Write each message with length prefix
131
for (String message : messages) {
132
byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
133
buffer.putInt(messageBytes.length); // Length prefix
134
buffer.put(messageBytes); // Message data
135
}
136
137
// Send the automatically sized buffer
138
buffer.flip();
139
session.write(buffer);
140
}
141
}
142
```
143
144
### Auto-Shrinking
145
146
```java { .api }
147
// Enable auto-shrinking to reclaim unused memory
148
IoBuffer buffer = IoBuffer.allocate(1024);
149
buffer.setAutoShrink(true);
150
151
// Fill buffer with data
152
byte[] data = new byte[500];
153
buffer.put(data);
154
buffer.flip();
155
156
// Process and clear data
157
processData(buffer);
158
buffer.clear();
159
160
// Buffer may shrink on next allocation if much smaller capacity is needed
161
// Auto-shrinking helps with memory efficiency in long-running applications
162
```
163
164
## Data Types and Operations
165
166
### Primitive Data Types
167
168
```java { .api }
169
// Write primitive types
170
IoBuffer buffer = IoBuffer.allocate(1024);
171
172
// Integers
173
buffer.putByte((byte) 42);
174
buffer.putShort((short) 1000);
175
buffer.putInt(123456);
176
buffer.putLong(123456789L);
177
178
// Floating point
179
buffer.putFloat(3.14f);
180
buffer.putDouble(2.71828);
181
182
// Character
183
buffer.putChar('A');
184
185
// Boolean (as byte)
186
buffer.put((byte) (true ? 1 : 0));
187
188
// Read primitive types
189
buffer.flip();
190
byte byteValue = buffer.get();
191
short shortValue = buffer.getShort();
192
int intValue = buffer.getInt();
193
long longValue = buffer.getLong();
194
float floatValue = buffer.getFloat();
195
double doubleValue = buffer.getDouble();
196
char charValue = buffer.getChar();
197
boolean boolValue = buffer.get() != 0;
198
```
199
200
### String Operations
201
202
```java { .api }
203
public class StringOperations {
204
205
public void stringExamples() throws CharacterCodingException {
206
IoBuffer buffer = IoBuffer.allocate(1024);
207
buffer.setAutoExpand(true);
208
209
// Write strings with different encodings
210
CharsetEncoder utf8Encoder = StandardCharsets.UTF_8.newEncoder();
211
CharsetEncoder utf16Encoder = StandardCharsets.UTF_16.newEncoder();
212
213
buffer.putString("Hello, World!", utf8Encoder);
214
buffer.putString("Привет, мир!", utf8Encoder); // Russian text
215
buffer.putString("こんにちは", utf16Encoder); // Japanese text
216
217
// Read strings back
218
buffer.flip();
219
CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder();
220
CharsetDecoder utf16Decoder = StandardCharsets.UTF_16.newDecoder();
221
222
String hello = buffer.getString(utf8Decoder);
223
String russian = buffer.getString(utf8Decoder);
224
String japanese = buffer.getString(utf16Decoder);
225
226
System.out.println("English: " + hello);
227
System.out.println("Russian: " + russian);
228
System.out.println("Japanese: " + japanese);
229
}
230
231
public void fixedLengthStrings() throws CharacterCodingException {
232
IoBuffer buffer = IoBuffer.allocate(100);
233
234
CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
235
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
236
237
// Write fixed-length string (padded/truncated to exact size)
238
String name = "John Doe";
239
buffer.putString(name, 20, encoder); // Always writes exactly 20 bytes
240
241
// Read fixed-length string
242
buffer.flip();
243
String readName = buffer.getString(20, decoder).trim();
244
System.out.println("Name: " + readName);
245
}
246
247
public void nullTerminatedStrings() throws CharacterCodingException {
248
IoBuffer buffer = IoBuffer.allocate(100);
249
250
CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
251
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
252
253
// Write null-terminated string
254
buffer.putString("Hello", encoder);
255
buffer.put((byte) 0); // Null terminator
256
257
// Read until null terminator
258
buffer.flip();
259
StringBuilder sb = new StringBuilder();
260
while (buffer.hasRemaining()) {
261
byte b = buffer.get();
262
if (b == 0) break;
263
sb.append((char) b);
264
}
265
266
System.out.println("Null-terminated string: " + sb.toString());
267
}
268
}
269
```
270
271
### Binary Data Operations
272
273
```java { .api }
274
public class BinaryOperations {
275
276
public void binaryDataExample() {
277
IoBuffer buffer = IoBuffer.allocate(1024);
278
279
// Write binary protocol header
280
buffer.put((byte) 0x01); // Version
281
buffer.put((byte) 0x02); // Message type
282
buffer.putShort((short) 1000); // Message length
283
buffer.putInt(System.currentTimeMillis()); // Timestamp
284
285
// Write payload
286
byte[] payload = createPayload();
287
buffer.put(payload);
288
289
// Update length field
290
int currentPos = buffer.position();
291
int messageLength = currentPos - 4; // Exclude header
292
buffer.putShort(2, (short) messageLength);
293
294
buffer.flip();
295
processMessage(buffer);
296
}
297
298
public void bitManipulation(IoBuffer buffer) {
299
// Pack multiple boolean flags into a single byte
300
boolean flag1 = true;
301
boolean flag2 = false;
302
boolean flag3 = true;
303
boolean flag4 = false;
304
305
byte flags = 0;
306
if (flag1) flags |= 0x01;
307
if (flag2) flags |= 0x02;
308
if (flag3) flags |= 0x04;
309
if (flag4) flags |= 0x08;
310
311
buffer.put(flags);
312
313
// Unpack flags
314
buffer.flip();
315
byte packedFlags = buffer.get();
316
boolean unpackedFlag1 = (packedFlags & 0x01) != 0;
317
boolean unpackedFlag2 = (packedFlags & 0x02) != 0;
318
boolean unpackedFlag3 = (packedFlags & 0x04) != 0;
319
boolean unpackedFlag4 = (packedFlags & 0x08) != 0;
320
}
321
}
322
```
323
324
## Buffer Utilities
325
326
### Hex Dump and Debugging
327
328
```java { .api }
329
public class BufferDebugging {
330
331
public void debugBuffer(IoBuffer buffer) {
332
System.out.println("=== Buffer Debug Information ===");
333
System.out.println("Capacity: " + buffer.capacity());
334
System.out.println("Position: " + buffer.position());
335
System.out.println("Limit: " + buffer.limit());
336
System.out.println("Remaining: " + buffer.remaining());
337
System.out.println("Auto-expand: " + buffer.isAutoExpand());
338
System.out.println("Auto-shrink: " + buffer.isAutoShrink());
339
System.out.println("Direct: " + buffer.isDirect());
340
System.out.println("Read-only: " + buffer.isReadOnly());
341
342
// Print hex dump
343
System.out.println("Hex dump:");
344
System.out.println(buffer.getHexDump());
345
346
// Print limited hex dump for large buffers
347
System.out.println("Hex dump (first 256 bytes):");
348
System.out.println(buffer.getHexDump(256));
349
}
350
351
public void compareBuffers(IoBuffer buffer1, IoBuffer buffer2) {
352
// Compare buffer contents
353
int comparison = buffer1.compareTo(buffer2);
354
355
if (comparison == 0) {
356
System.out.println("Buffers are identical");
357
} else if (comparison < 0) {
358
System.out.println("Buffer1 is lexicographically less than Buffer2");
359
} else {
360
System.out.println("Buffer1 is lexicographically greater than Buffer2");
361
}
362
363
// Check if buffers have same content
364
if (buffer1.equals(buffer2)) {
365
System.out.println("Buffers have same content");
366
}
367
}
368
}
369
```
370
371
### Buffer Pooling and Allocation
372
373
```java { .api }
374
// Custom buffer allocator for performance optimization
375
public class CustomBufferAllocator implements IoBufferAllocator {
376
private final boolean useDirectBuffer;
377
378
public CustomBufferAllocator(boolean useDirectBuffer) {
379
this.useDirectBuffer = useDirectBuffer;
380
}
381
382
@Override
383
public IoBuffer allocate(int capacity) {
384
return wrap(ByteBuffer.allocate(capacity));
385
}
386
387
@Override
388
public IoBuffer allocate(int capacity, boolean direct) {
389
ByteBuffer nioBuffer;
390
if (direct || useDirectBuffer) {
391
nioBuffer = ByteBuffer.allocateDirect(capacity);
392
} else {
393
nioBuffer = ByteBuffer.allocate(capacity);
394
}
395
return wrap(nioBuffer);
396
}
397
398
@Override
399
public IoBuffer wrap(ByteBuffer nioBuffer) {
400
return new CustomIoBuffer(nioBuffer);
401
}
402
403
@Override
404
public void dispose() {
405
// Cleanup resources if needed
406
}
407
}
408
409
// Set custom allocator
410
IoBuffer.setAllocator(new CustomBufferAllocator(true));
411
412
// Cached buffer allocator for better performance
413
CachedBufferAllocator cachedAllocator = new CachedBufferAllocator();
414
IoBuffer.setAllocator(cachedAllocator);
415
```
416
417
## Advanced Buffer Operations
418
419
### Buffer Slicing and Views
420
421
```java { .api }
422
public class BufferViews {
423
424
public void bufferSlicing() {
425
// Create original buffer with data
426
IoBuffer original = IoBuffer.allocate(100);
427
for (int i = 0; i < 50; i++) {
428
original.putInt(i);
429
}
430
431
// Create slice starting at position 20
432
original.position(20);
433
IoBuffer slice = original.slice();
434
435
// Slice shares data with original but has independent position/limit
436
slice.putInt(999); // Modifies original buffer at position 20
437
438
// Create duplicate with same content but independent position
439
original.rewind();
440
IoBuffer duplicate = original.duplicate();
441
442
// Create read-only view
443
IoBuffer readOnly = original.asReadOnlyBuffer();
444
445
// Read-only buffer throws exception on write attempts
446
try {
447
readOnly.putInt(123);
448
} catch (ReadOnlyBufferException e) {
449
System.out.println("Cannot write to read-only buffer");
450
}
451
}
452
453
public void bufferTypeViews() {
454
IoBuffer buffer = IoBuffer.allocate(100);
455
456
// Get typed views of buffer content
457
IntBuffer intView = buffer.asIntBuffer();
458
ShortBuffer shortView = buffer.asShortBuffer();
459
LongBuffer longView = buffer.asLongBuffer();
460
FloatBuffer floatView = buffer.asFloatBuffer();
461
DoubleBuffer doubleView = buffer.asDoubleBuffer();
462
CharBuffer charView = buffer.asCharBuffer();
463
464
// Use typed view for bulk operations
465
int[] intArray = {1, 2, 3, 4, 5};
466
intView.put(intArray);
467
468
// Read back using view
469
intView.rewind();
470
int[] readBack = new int[5];
471
intView.get(readBack);
472
}
473
}
474
```
475
476
### Stream Integration
477
478
```java { .api }
479
public class StreamIntegration {
480
481
public void bufferToStream() throws IOException {
482
IoBuffer buffer = IoBuffer.allocate(1024);
483
484
// Write data to buffer
485
buffer.putString("Hello, Stream!", StandardCharsets.UTF_8.newEncoder());
486
buffer.flip();
487
488
// Convert buffer to InputStream
489
InputStream inputStream = buffer.asInputStream();
490
491
// Read from stream
492
byte[] data = new byte[buffer.remaining()];
493
inputStream.read(data);
494
495
String result = new String(data, StandardCharsets.UTF_8);
496
System.out.println("Read from stream: " + result);
497
}
498
499
public void streamToBuffer() throws IOException {
500
// Create buffer as OutputStream
501
IoBuffer buffer = IoBuffer.allocate(1024);
502
buffer.setAutoExpand(true);
503
504
OutputStream outputStream = buffer.asOutputStream();
505
506
// Write to buffer via stream
507
String message = "Hello from OutputStream!";
508
outputStream.write(message.getBytes(StandardCharsets.UTF_8));
509
510
// Prepare buffer for reading
511
buffer.flip();
512
513
// Read the data
514
byte[] result = new byte[buffer.remaining()];
515
buffer.get(result);
516
517
System.out.println("Written via stream: " + new String(result, StandardCharsets.UTF_8));
518
}
519
}
520
```
521
522
### Memory-Mapped Buffers
523
524
```java { .api }
525
public class MemoryMappedBuffers {
526
527
public void createMappedBuffer(String filename) throws IOException {
528
try (RandomAccessFile file = new RandomAccessFile(filename, "rw")) {
529
FileChannel channel = file.getChannel();
530
531
// Create memory-mapped buffer
532
MappedByteBuffer mappedBuffer = channel.map(
533
FileChannel.MapMode.READ_WRITE, 0, 1024);
534
535
// Wrap in IoBuffer
536
IoBuffer buffer = IoBuffer.wrap(mappedBuffer);
537
538
// Write data directly to file via buffer
539
buffer.putString("Memory-mapped data", StandardCharsets.UTF_8.newEncoder());
540
541
// Force write to disk
542
if (mappedBuffer instanceof MappedByteBuffer) {
543
((MappedByteBuffer) buffer.buf()).force();
544
}
545
}
546
}
547
}
548
```
549
550
## Buffer Performance Optimization
551
552
### Efficient Buffer Usage
553
554
```java { .api }
555
public class BufferPerformance {
556
557
public void efficientBufferReuse() {
558
// Reuse buffers to reduce GC pressure
559
IoBuffer buffer = IoBuffer.allocate(1024);
560
561
for (int i = 0; i < 1000; i++) {
562
buffer.clear(); // Reset for reuse
563
564
// Write data
565
buffer.putInt(i);
566
buffer.putString("Message " + i, StandardCharsets.UTF_8.newEncoder());
567
568
// Process buffer
569
buffer.flip();
570
processBuffer(buffer);
571
572
// Buffer is reused in next iteration
573
}
574
}
575
576
public void bulkOperations() {
577
IoBuffer source = IoBuffer.allocate(10000);
578
IoBuffer destination = IoBuffer.allocate(10000);
579
580
// Fill source with data
581
byte[] data = new byte[5000];
582
Arrays.fill(data, (byte) 42);
583
source.put(data);
584
585
// Bulk copy is more efficient than individual byte operations
586
source.flip();
587
destination.put(source); // Bulk copy
588
589
// Instead of:
590
// while (source.hasRemaining()) {
591
// destination.put(source.get()); // Inefficient
592
// }
593
}
594
595
public void directBufferBenefits() {
596
// Direct buffers are more efficient for I/O operations
597
IoBuffer directBuffer = IoBuffer.allocate(1024, true);
598
IoBuffer heapBuffer = IoBuffer.allocate(1024, false);
599
600
// Direct buffer: no copying between Java heap and native memory
601
// Heap buffer: requires copying for I/O operations
602
603
// Use direct buffers for:
604
// - Large buffers
605
// - Long-lived buffers
606
// - High I/O throughput scenarios
607
608
// Use heap buffers for:
609
// - Small, short-lived buffers
610
// - Frequent allocation/deallocation
611
// - When avoiding off-heap memory limits
612
}
613
}
614
```
615
616
### Buffer Size Strategies
617
618
```java { .api }
619
public class BufferSizing {
620
621
public IoBuffer createOptimalBuffer(int estimatedSize) {
622
// Start with estimated size
623
int initialSize = Math.max(estimatedSize, 64); // Minimum 64 bytes
624
625
// Round up to next power of 2 for better memory alignment
626
int optimalSize = Integer.highestOneBit(initialSize - 1) << 1;
627
628
IoBuffer buffer = IoBuffer.allocate(optimalSize);
629
buffer.setAutoExpand(true); // Allow growth if needed
630
631
return buffer;
632
}
633
634
public void adaptiveBufferSizing(IoSession session) {
635
// Adapt buffer size based on session history
636
Long avgMessageSize = (Long) session.getAttribute("avgMessageSize");
637
638
if (avgMessageSize == null) {
639
avgMessageSize = 1024L; // Default size
640
}
641
642
// Create buffer with 50% headroom
643
int bufferSize = (int) (avgMessageSize * 1.5);
644
IoBuffer buffer = IoBuffer.allocate(bufferSize);
645
buffer.setAutoExpand(true);
646
647
// Update average after processing
648
updateAverageMessageSize(session, buffer.position());
649
}
650
651
private void updateAverageMessageSize(IoSession session, int currentSize) {
652
Long avgSize = (Long) session.getAttribute("avgMessageSize");
653
Long messageCount = (Long) session.getAttribute("messageCount");
654
655
if (avgSize == null) {
656
avgSize = (long) currentSize;
657
messageCount = 1L;
658
} else {
659
messageCount++;
660
avgSize = ((avgSize * (messageCount - 1)) + currentSize) / messageCount;
661
}
662
663
session.setAttribute("avgMessageSize", avgSize);
664
session.setAttribute("messageCount", messageCount);
665
}
666
}
667
```
668
669
IoBuffer provides a powerful and flexible foundation for handling binary data in MINA applications, with features that significantly simplify network programming compared to raw ByteBuffer usage.