83 lines
3.2 KiB
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;
|
|
}
|
|
} |