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

89 lines
3.4 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.
*
*
* Created by jjenkov on 18-10-2015.
*/
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 = 1024 * KB;
//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){
this.smallMessageBufferFreeBlocks.put(i);
}
for(int i=0; i<mediumMessageBuffer.length; i+= CAPACITY_MEDIUM){
this.mediumMessageBufferFreeBlocks.put(i);
}
for(int i=0; i<largeMessageBuffer.length; i+= CAPACITY_LARGE){
this.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, this.smallMessageBufferFreeBlocks, this.mediumMessageBufferFreeBlocks, this.mediumMessageBuffer, CAPACITY_MEDIUM);
} else if(message.capacity == CAPACITY_MEDIUM){
return moveMessage(message, this.mediumMessageBufferFreeBlocks, this.largeMessageBufferFreeBlocks, this.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;
}
}