CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apache-mina--mina-core

Apache MINA Core is a network application framework for building high-performance, scalable network applications with event-driven, asynchronous I/O over various transports including TCP/IP and UDP/IP via Java NIO.

Pending
Overview
Eval results
Files

buffer-management.mddocs/

Buffer Management

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.

IoBuffer Basics

Core Interface

public abstract class IoBuffer implements Comparable<IoBuffer> {
    // Allocation methods
    static IoBuffer allocate(int capacity);
    static IoBuffer allocate(int capacity, boolean direct);
    static IoBuffer wrap(byte[] array);
    static IoBuffer wrap(ByteBuffer nioBuffer);
    
    // Buffer properties
    int capacity();
    int position();
    IoBuffer position(int newPosition);
    int limit();
    IoBuffer limit(int newLimit);
    int remaining();
    boolean hasRemaining();
    
    // Auto-expansion
    boolean isAutoExpand();
    IoBuffer setAutoExpand(boolean autoExpand);
    boolean isAutoShrink();
    IoBuffer setAutoShrink(boolean autoShrink);
    
    // Buffer manipulation
    IoBuffer clear();
    IoBuffer flip();
    IoBuffer rewind();
    IoBuffer mark();
    IoBuffer reset();
    IoBuffer compact();
    
    // Data access
    byte get();
    IoBuffer put(byte b);
    byte get(int index);
    IoBuffer put(int index, byte b);
    
    // Bulk operations
    IoBuffer get(byte[] dst);
    IoBuffer put(byte[] src);
    IoBuffer get(byte[] dst, int offset, int length);
    IoBuffer put(byte[] src, int offset, int length);
    
    // String operations
    String getString(CharsetDecoder decoder) throws CharacterCodingException;
    String getString(int fieldSize, CharsetDecoder decoder) throws CharacterCodingException;
    IoBuffer putString(String value, CharsetEncoder encoder) throws CharacterCodingException;
    String getHexDump();
    String getHexDump(int lengthLimit);
}

Buffer Allocation

Basic Allocation

// Allocate heap buffer
IoBuffer buffer = IoBuffer.allocate(1024);

// Allocate direct buffer (off-heap)
IoBuffer directBuffer = IoBuffer.allocate(1024, true);

// Set default buffer type
IoBuffer.setUseDirectBuffer(true);  // Use direct buffers by default
IoBuffer defaultBuffer = IoBuffer.allocate(1024); // Now allocates direct buffer

// Check if buffer is direct
if (buffer.isDirect()) {
    System.out.println("Using direct buffer");
} else {
    System.out.println("Using heap buffer");
}

Buffer Wrapping

// Wrap byte array
byte[] data = "Hello, World!".getBytes("UTF-8");
IoBuffer wrapped = IoBuffer.wrap(data);

// Wrap NIO ByteBuffer
ByteBuffer nioBuffer = ByteBuffer.allocate(1024);
IoBuffer fromNio = IoBuffer.wrap(nioBuffer);

// Wrap with automatic expansion
IoBuffer autoExpanding = IoBuffer.wrap(data);
autoExpanding.setAutoExpand(true);

// Create buffer from existing buffer
IoBuffer original = IoBuffer.allocate(100);
IoBuffer duplicate = original.duplicate();
IoBuffer slice = original.slice();
IoBuffer readOnlyView = original.asReadOnlyBuffer();

Auto-Expansion and Auto-Shrinking

Auto-Expansion

// Enable auto-expansion to handle variable-length data
IoBuffer buffer = IoBuffer.allocate(16);
buffer.setAutoExpand(true);

// Write data without worrying about capacity
String longMessage = "This is a very long message that exceeds the initial buffer capacity";
buffer.putString(longMessage, Charset.forName("UTF-8").newEncoder());

System.out.println("Buffer capacity grew to: " + buffer.capacity());

// Auto-expansion in action
public class AutoExpandExample {
    
    public void writeVariableLengthData(IoSession session, List<String> messages) {
        IoBuffer buffer = IoBuffer.allocate(64);
        buffer.setAutoExpand(true);
        
        // Write message count
        buffer.putInt(messages.size());
        
        // Write each message with length prefix
        for (String message : messages) {
            byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
            buffer.putInt(messageBytes.length);  // Length prefix
            buffer.put(messageBytes);            // Message data
        }
        
        // Send the automatically sized buffer
        buffer.flip();
        session.write(buffer);
    }
}

Auto-Shrinking

// Enable auto-shrinking to reclaim unused memory
IoBuffer buffer = IoBuffer.allocate(1024);
buffer.setAutoShrink(true);

// Fill buffer with data
byte[] data = new byte[500];
buffer.put(data);
buffer.flip();

// Process and clear data
processData(buffer);
buffer.clear();

// Buffer may shrink on next allocation if much smaller capacity is needed
// Auto-shrinking helps with memory efficiency in long-running applications

Data Types and Operations

Primitive Data Types

// Write primitive types
IoBuffer buffer = IoBuffer.allocate(1024);

// Integers
buffer.putByte((byte) 42);
buffer.putShort((short) 1000);
buffer.putInt(123456);
buffer.putLong(123456789L);

// Floating point
buffer.putFloat(3.14f);
buffer.putDouble(2.71828);

// Character
buffer.putChar('A');

// Boolean (as byte)
buffer.put((byte) (true ? 1 : 0));

// Read primitive types
buffer.flip();
byte byteValue = buffer.get();
short shortValue = buffer.getShort();
int intValue = buffer.getInt();
long longValue = buffer.getLong();
float floatValue = buffer.getFloat();
double doubleValue = buffer.getDouble();
char charValue = buffer.getChar();
boolean boolValue = buffer.get() != 0;

String Operations

public class StringOperations {
    
    public void stringExamples() throws CharacterCodingException {
        IoBuffer buffer = IoBuffer.allocate(1024);
        buffer.setAutoExpand(true);
        
        // Write strings with different encodings
        CharsetEncoder utf8Encoder = StandardCharsets.UTF_8.newEncoder();
        CharsetEncoder utf16Encoder = StandardCharsets.UTF_16.newEncoder();
        
        buffer.putString("Hello, World!", utf8Encoder);
        buffer.putString("Привет, мир!", utf8Encoder);  // Russian text
        buffer.putString("こんにちは", utf16Encoder);      // Japanese text
        
        // Read strings back
        buffer.flip();
        CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder();
        CharsetDecoder utf16Decoder = StandardCharsets.UTF_16.newDecoder();
        
        String hello = buffer.getString(utf8Decoder);
        String russian = buffer.getString(utf8Decoder);
        String japanese = buffer.getString(utf16Decoder);
        
        System.out.println("English: " + hello);
        System.out.println("Russian: " + russian);
        System.out.println("Japanese: " + japanese);
    }
    
    public void fixedLengthStrings() throws CharacterCodingException {
        IoBuffer buffer = IoBuffer.allocate(100);
        
        CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
        
        // Write fixed-length string (padded/truncated to exact size)
        String name = "John Doe";
        buffer.putString(name, 20, encoder);  // Always writes exactly 20 bytes
        
        // Read fixed-length string
        buffer.flip();
        String readName = buffer.getString(20, decoder).trim();
        System.out.println("Name: " + readName);
    }
    
    public void nullTerminatedStrings() throws CharacterCodingException {
        IoBuffer buffer = IoBuffer.allocate(100);
        
        CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
        
        // Write null-terminated string
        buffer.putString("Hello", encoder);
        buffer.put((byte) 0);  // Null terminator
        
        // Read until null terminator
        buffer.flip();
        StringBuilder sb = new StringBuilder();
        while (buffer.hasRemaining()) {
            byte b = buffer.get();
            if (b == 0) break;
            sb.append((char) b);
        }
        
        System.out.println("Null-terminated string: " + sb.toString());
    }
}

Binary Data Operations

public class BinaryOperations {
    
    public void binaryDataExample() {
        IoBuffer buffer = IoBuffer.allocate(1024);
        
        // Write binary protocol header
        buffer.put((byte) 0x01);           // Version
        buffer.put((byte) 0x02);           // Message type
        buffer.putShort((short) 1000);     // Message length
        buffer.putInt(System.currentTimeMillis()); // Timestamp
        
        // Write payload
        byte[] payload = createPayload();
        buffer.put(payload);
        
        // Update length field
        int currentPos = buffer.position();
        int messageLength = currentPos - 4; // Exclude header
        buffer.putShort(2, (short) messageLength);
        
        buffer.flip();
        processMessage(buffer);
    }
    
    public void bitManipulation(IoBuffer buffer) {
        // Pack multiple boolean flags into a single byte
        boolean flag1 = true;
        boolean flag2 = false;
        boolean flag3 = true;
        boolean flag4 = false;
        
        byte flags = 0;
        if (flag1) flags |= 0x01;
        if (flag2) flags |= 0x02;
        if (flag3) flags |= 0x04;
        if (flag4) flags |= 0x08;
        
        buffer.put(flags);
        
        // Unpack flags
        buffer.flip();
        byte packedFlags = buffer.get();
        boolean unpackedFlag1 = (packedFlags & 0x01) != 0;
        boolean unpackedFlag2 = (packedFlags & 0x02) != 0;
        boolean unpackedFlag3 = (packedFlags & 0x04) != 0;
        boolean unpackedFlag4 = (packedFlags & 0x08) != 0;
    }
}

Buffer Utilities

Hex Dump and Debugging

public class BufferDebugging {
    
    public void debugBuffer(IoBuffer buffer) {
        System.out.println("=== Buffer Debug Information ===");
        System.out.println("Capacity: " + buffer.capacity());
        System.out.println("Position: " + buffer.position());
        System.out.println("Limit: " + buffer.limit());
        System.out.println("Remaining: " + buffer.remaining());
        System.out.println("Auto-expand: " + buffer.isAutoExpand());
        System.out.println("Auto-shrink: " + buffer.isAutoShrink());
        System.out.println("Direct: " + buffer.isDirect());
        System.out.println("Read-only: " + buffer.isReadOnly());
        
        // Print hex dump
        System.out.println("Hex dump:");
        System.out.println(buffer.getHexDump());
        
        // Print limited hex dump for large buffers
        System.out.println("Hex dump (first 256 bytes):");
        System.out.println(buffer.getHexDump(256));
    }
    
    public void compareBuffers(IoBuffer buffer1, IoBuffer buffer2) {
        // Compare buffer contents
        int comparison = buffer1.compareTo(buffer2);
        
        if (comparison == 0) {
            System.out.println("Buffers are identical");
        } else if (comparison < 0) {
            System.out.println("Buffer1 is lexicographically less than Buffer2");
        } else {
            System.out.println("Buffer1 is lexicographically greater than Buffer2");
        }
        
        // Check if buffers have same content
        if (buffer1.equals(buffer2)) {
            System.out.println("Buffers have same content");
        }
    }
}

Buffer Pooling and Allocation

// Custom buffer allocator for performance optimization
public class CustomBufferAllocator implements IoBufferAllocator {
    private final boolean useDirectBuffer;
    
    public CustomBufferAllocator(boolean useDirectBuffer) {
        this.useDirectBuffer = useDirectBuffer;
    }
    
    @Override
    public IoBuffer allocate(int capacity) {
        return wrap(ByteBuffer.allocate(capacity));
    }
    
    @Override
    public IoBuffer allocate(int capacity, boolean direct) {
        ByteBuffer nioBuffer;
        if (direct || useDirectBuffer) {
            nioBuffer = ByteBuffer.allocateDirect(capacity);
        } else {
            nioBuffer = ByteBuffer.allocate(capacity);
        }
        return wrap(nioBuffer);
    }
    
    @Override
    public IoBuffer wrap(ByteBuffer nioBuffer) {
        return new CustomIoBuffer(nioBuffer);
    }
    
    @Override
    public void dispose() {
        // Cleanup resources if needed
    }
}

// Set custom allocator
IoBuffer.setAllocator(new CustomBufferAllocator(true));

// Cached buffer allocator for better performance
CachedBufferAllocator cachedAllocator = new CachedBufferAllocator();
IoBuffer.setAllocator(cachedAllocator);

Advanced Buffer Operations

Buffer Slicing and Views

public class BufferViews {
    
    public void bufferSlicing() {
        // Create original buffer with data
        IoBuffer original = IoBuffer.allocate(100);
        for (int i = 0; i < 50; i++) {
            original.putInt(i);
        }
        
        // Create slice starting at position 20
        original.position(20);
        IoBuffer slice = original.slice();
        
        // Slice shares data with original but has independent position/limit
        slice.putInt(999);  // Modifies original buffer at position 20
        
        // Create duplicate with same content but independent position
        original.rewind();
        IoBuffer duplicate = original.duplicate();
        
        // Create read-only view
        IoBuffer readOnly = original.asReadOnlyBuffer();
        
        // Read-only buffer throws exception on write attempts
        try {
            readOnly.putInt(123);
        } catch (ReadOnlyBufferException e) {
            System.out.println("Cannot write to read-only buffer");
        }
    }
    
    public void bufferTypeViews() {
        IoBuffer buffer = IoBuffer.allocate(100);
        
        // Get typed views of buffer content
        IntBuffer intView = buffer.asIntBuffer();
        ShortBuffer shortView = buffer.asShortBuffer();
        LongBuffer longView = buffer.asLongBuffer();
        FloatBuffer floatView = buffer.asFloatBuffer();
        DoubleBuffer doubleView = buffer.asDoubleBuffer();
        CharBuffer charView = buffer.asCharBuffer();
        
        // Use typed view for bulk operations
        int[] intArray = {1, 2, 3, 4, 5};
        intView.put(intArray);
        
        // Read back using view
        intView.rewind();
        int[] readBack = new int[5];
        intView.get(readBack);
    }
}

Stream Integration

public class StreamIntegration {
    
    public void bufferToStream() throws IOException {
        IoBuffer buffer = IoBuffer.allocate(1024);
        
        // Write data to buffer
        buffer.putString("Hello, Stream!", StandardCharsets.UTF_8.newEncoder());
        buffer.flip();
        
        // Convert buffer to InputStream
        InputStream inputStream = buffer.asInputStream();
        
        // Read from stream
        byte[] data = new byte[buffer.remaining()];
        inputStream.read(data);
        
        String result = new String(data, StandardCharsets.UTF_8);
        System.out.println("Read from stream: " + result);
    }
    
    public void streamToBuffer() throws IOException {
        // Create buffer as OutputStream
        IoBuffer buffer = IoBuffer.allocate(1024);
        buffer.setAutoExpand(true);
        
        OutputStream outputStream = buffer.asOutputStream();
        
        // Write to buffer via stream
        String message = "Hello from OutputStream!";
        outputStream.write(message.getBytes(StandardCharsets.UTF_8));
        
        // Prepare buffer for reading
        buffer.flip();
        
        // Read the data
        byte[] result = new byte[buffer.remaining()];
        buffer.get(result);
        
        System.out.println("Written via stream: " + new String(result, StandardCharsets.UTF_8));
    }
}

Memory-Mapped Buffers

public class MemoryMappedBuffers {
    
    public void createMappedBuffer(String filename) throws IOException {
        try (RandomAccessFile file = new RandomAccessFile(filename, "rw")) {
            FileChannel channel = file.getChannel();
            
            // Create memory-mapped buffer
            MappedByteBuffer mappedBuffer = channel.map(
                FileChannel.MapMode.READ_WRITE, 0, 1024);
            
            // Wrap in IoBuffer
            IoBuffer buffer = IoBuffer.wrap(mappedBuffer);
            
            // Write data directly to file via buffer
            buffer.putString("Memory-mapped data", StandardCharsets.UTF_8.newEncoder());
            
            // Force write to disk
            if (mappedBuffer instanceof MappedByteBuffer) {
                ((MappedByteBuffer) buffer.buf()).force();
            }
        }
    }
}

Buffer Performance Optimization

Efficient Buffer Usage

public class BufferPerformance {
    
    public void efficientBufferReuse() {
        // Reuse buffers to reduce GC pressure
        IoBuffer buffer = IoBuffer.allocate(1024);
        
        for (int i = 0; i < 1000; i++) {
            buffer.clear();  // Reset for reuse
            
            // Write data
            buffer.putInt(i);
            buffer.putString("Message " + i, StandardCharsets.UTF_8.newEncoder());
            
            // Process buffer
            buffer.flip();
            processBuffer(buffer);
            
            // Buffer is reused in next iteration
        }
    }
    
    public void bulkOperations() {
        IoBuffer source = IoBuffer.allocate(10000);
        IoBuffer destination = IoBuffer.allocate(10000);
        
        // Fill source with data
        byte[] data = new byte[5000];
        Arrays.fill(data, (byte) 42);
        source.put(data);
        
        // Bulk copy is more efficient than individual byte operations
        source.flip();
        destination.put(source);  // Bulk copy
        
        // Instead of:
        // while (source.hasRemaining()) {
        //     destination.put(source.get());  // Inefficient
        // }
    }
    
    public void directBufferBenefits() {
        // Direct buffers are more efficient for I/O operations
        IoBuffer directBuffer = IoBuffer.allocate(1024, true);
        IoBuffer heapBuffer = IoBuffer.allocate(1024, false);
        
        // Direct buffer: no copying between Java heap and native memory
        // Heap buffer: requires copying for I/O operations
        
        // Use direct buffers for:
        // - Large buffers
        // - Long-lived buffers
        // - High I/O throughput scenarios
        
        // Use heap buffers for:
        // - Small, short-lived buffers
        // - Frequent allocation/deallocation
        // - When avoiding off-heap memory limits
    }
}

Buffer Size Strategies

public class BufferSizing {
    
    public IoBuffer createOptimalBuffer(int estimatedSize) {
        // Start with estimated size
        int initialSize = Math.max(estimatedSize, 64); // Minimum 64 bytes
        
        // Round up to next power of 2 for better memory alignment
        int optimalSize = Integer.highestOneBit(initialSize - 1) << 1;
        
        IoBuffer buffer = IoBuffer.allocate(optimalSize);
        buffer.setAutoExpand(true);  // Allow growth if needed
        
        return buffer;
    }
    
    public void adaptiveBufferSizing(IoSession session) {
        // Adapt buffer size based on session history
        Long avgMessageSize = (Long) session.getAttribute("avgMessageSize");
        
        if (avgMessageSize == null) {
            avgMessageSize = 1024L; // Default size
        }
        
        // Create buffer with 50% headroom
        int bufferSize = (int) (avgMessageSize * 1.5);
        IoBuffer buffer = IoBuffer.allocate(bufferSize);
        buffer.setAutoExpand(true);
        
        // Update average after processing
        updateAverageMessageSize(session, buffer.position());
    }
    
    private void updateAverageMessageSize(IoSession session, int currentSize) {
        Long avgSize = (Long) session.getAttribute("avgMessageSize");
        Long messageCount = (Long) session.getAttribute("messageCount");
        
        if (avgSize == null) {
            avgSize = (long) currentSize;
            messageCount = 1L;
        } else {
            messageCount++;
            avgSize = ((avgSize * (messageCount - 1)) + currentSize) / messageCount;
        }
        
        session.setAttribute("avgMessageSize", avgSize);
        session.setAttribute("messageCount", messageCount);
    }
}

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.

Install with Tessl CLI

npx tessl i tessl/maven-org-apache-mina--mina-core

docs

async-operations.md

buffer-management.md

filter-chain.md

index.md

protocol-codecs.md

service-abstractions.md

session-management.md

transport-layer.md

tile.json