This repository has been archived on 2021-12-05. You can view files and clone it, but cannot push or open issues or pull requests.
java-nio-server/src/main/java/com/jenkov/nioserver/MessageBuffer.java

83 lines
3.2 KiB
Java

package com.jenkov.nioserver;
/**
* A shared buffer which can contain many messages inside. A message gets a
* section of the buffer to use. If the message outgrows the section in size,
* the message requests a larger section and the message is copied to that
* larger section. The smaller section is then freed again.<br>
* <br>
* Project: <strong>java-nio-server</strong><br>
* File: <strong>MessageBuffer.java</strong><br>
* Created: <strong>18 Oct 2015</strong><br>
*
* @author jjenkov
*/
public class MessageBuffer {
public static int KB = 1024;
public static int MB = 1024 * KB;
private static final int CAPACITY_SMALL = 4 * KB;
private static final int CAPACITY_MEDIUM = 128 * KB;
private static final int CAPACITY_LARGE = 1 * MB;
// package scope (default) - so they can be accessed from unit tests.
byte[] smallMessageBuffer = new byte[1024 * 4 * KB]; // 1024 x 4KB messages = 4MB.
byte[] mediumMessageBuffer = new byte[128 * 128 * KB]; // 128 x 128KB messages = 16MB.
byte[] largeMessageBuffer = new byte[16 * 1 * MB]; // 16 * 1MB messages = 16MB.
QueueIntFlip smallMessageBufferFreeBlocks = new QueueIntFlip(1024); // 1024 free sections
QueueIntFlip mediumMessageBufferFreeBlocks = new QueueIntFlip(128); // 128 free sections
QueueIntFlip largeMessageBufferFreeBlocks = new QueueIntFlip(16); // 16 free sections
// TODO: make all message buffer capacities and block sizes configurable
// TODO: calculate free block queue sizes based on capacity and block size of
// buffers.
public MessageBuffer() {
// add all free sections to all free section queues.
for (int i = 0; i < smallMessageBuffer.length; i += CAPACITY_SMALL)
smallMessageBufferFreeBlocks.put(i);
for (int i = 0; i < mediumMessageBuffer.length; i += CAPACITY_MEDIUM)
mediumMessageBufferFreeBlocks.put(i);
for (int i = 0; i < largeMessageBuffer.length; i += CAPACITY_LARGE)
largeMessageBufferFreeBlocks.put(i);
}
public Message getMessage() {
int nextFreeSmallBlock = this.smallMessageBufferFreeBlocks.take();
if (nextFreeSmallBlock == -1) return null;
Message message = new Message(this); // TODO: get from Message pool - caps memory usage.
message.sharedArray = this.smallMessageBuffer;
message.capacity = CAPACITY_SMALL;
message.offset = nextFreeSmallBlock;
message.length = 0;
return message;
}
public boolean expandMessage(Message message) {
if (message.capacity == CAPACITY_SMALL)
return moveMessage(message, smallMessageBufferFreeBlocks, mediumMessageBufferFreeBlocks, mediumMessageBuffer, CAPACITY_MEDIUM);
else if (message.capacity == CAPACITY_MEDIUM)
return moveMessage(message, mediumMessageBufferFreeBlocks, largeMessageBufferFreeBlocks, largeMessageBuffer, CAPACITY_LARGE);
else return false;
}
private boolean moveMessage(Message message, QueueIntFlip srcBlockQueue, QueueIntFlip destBlockQueue, byte[] dest, int newCapacity) {
int nextFreeBlock = destBlockQueue.take();
if (nextFreeBlock == -1) return false;
System.arraycopy(message.sharedArray, message.offset, dest, nextFreeBlock, message.length);
srcBlockQueue.put(message.offset); // free smaller block after copy
message.sharedArray = dest;
message.offset = nextFreeBlock;
message.capacity = newCapacity;
return true;
}
}