mirror of
https://github.com/tursom/TursomServer.git
synced 2025-01-01 07:50:47 +08:00
将AdvanceByteBuffer替换为更加简洁的ByteBuffer
This commit is contained in:
parent
be04d7ff73
commit
75483b45a1
55
AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThread.kt
Normal file
55
AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThread.kt
Normal file
@ -0,0 +1,55 @@
|
||||
package cn.tursom.niothread
|
||||
|
||||
import java.io.Closeable
|
||||
import java.nio.channels.SelectableChannel
|
||||
import java.nio.channels.SelectionKey
|
||||
import java.nio.channels.Selector
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
/**
|
||||
* 一个 nio 工作线程
|
||||
* 一个线程对应一个 Selector (选择器)
|
||||
*/
|
||||
interface NioThread : Closeable {
|
||||
val selector: Selector
|
||||
val closed: Boolean
|
||||
val timeout: Long
|
||||
val workLoop: (thread: NioThread, key: SelectionKey) -> Unit
|
||||
val thread: Thread
|
||||
val daemon: Boolean
|
||||
|
||||
fun wakeup() {
|
||||
if (Thread.currentThread() != thread) selector.wakeup()
|
||||
}
|
||||
|
||||
/**
|
||||
* 将通道注册到线程对应的选择器上
|
||||
*/
|
||||
fun register(channel: SelectableChannel, ops: Int, onComplete: (key: SelectionKey) -> Unit) {
|
||||
if (Thread.currentThread() == thread) {
|
||||
val key = channel.register(selector, ops)
|
||||
onComplete(key)
|
||||
} else {
|
||||
execute {
|
||||
val key = channel.register(selector, ops)
|
||||
onComplete(key)
|
||||
}
|
||||
wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
fun execute(command: Runnable) = execute(command::run)
|
||||
fun execute(command: () -> Unit)
|
||||
|
||||
fun <T> call(task: Callable<T>): T {
|
||||
return submit(task).get()
|
||||
}
|
||||
|
||||
fun <T> call(task: () -> T): T {
|
||||
return call(Callable<T> { task() })
|
||||
}
|
||||
|
||||
fun <T> submit(task: Callable<T>): NioThreadTaskFuture<T> = submit(task::call)
|
||||
fun <T> submit(task: () -> T): NioThreadTaskFuture<T>
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package cn.tursom.niothread
|
||||
|
||||
interface NioThreadTaskFuture<T> {
|
||||
fun get(): T
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package cn.tursom.niothread
|
||||
|
||||
import java.nio.channels.SelectableChannel
|
||||
import java.nio.channels.SelectionKey
|
||||
import java.nio.channels.Selector
|
||||
import java.util.concurrent.*
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class ThreadPoolNioThread(
|
||||
val threadName: String = "",
|
||||
override val selector: Selector = Selector.open(),
|
||||
override val daemon: Boolean = false,
|
||||
override val timeout: Long = 3000,
|
||||
override val workLoop: (thread: NioThread, key: SelectionKey) -> Unit
|
||||
) : NioThread {
|
||||
override lateinit var thread: Thread
|
||||
val threadPool: ExecutorService = ThreadPoolExecutor(1, 1,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
LinkedBlockingQueue<Runnable>(),
|
||||
ThreadFactory {
|
||||
val thread = Thread(it)
|
||||
this.thread = thread
|
||||
thread.isDaemon = daemon
|
||||
thread.name = threadName
|
||||
thread
|
||||
})
|
||||
override var closed: Boolean = false
|
||||
|
||||
init {
|
||||
threadPool.execute(object : Runnable {
|
||||
override fun run() {
|
||||
if (selector.isOpen) {
|
||||
if (selector.select(timeout) != 0) {
|
||||
val keyIter = selector.selectedKeys().iterator()
|
||||
while (keyIter.hasNext()) {
|
||||
val key = keyIter.next()
|
||||
keyIter.remove()
|
||||
workLoop(this@ThreadPoolNioThread, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!threadPool.isShutdown) threadPool.execute(this)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun wakeup() {
|
||||
if (Thread.currentThread() != thread) {
|
||||
selector.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
override fun register(channel: SelectableChannel, ops: Int, onComplete: (key: SelectionKey) -> Unit) {
|
||||
if (Thread.currentThread() == thread) {
|
||||
onComplete(channel.register(selector, ops))
|
||||
} else {
|
||||
threadPool.execute { register(channel, ops, onComplete) }
|
||||
wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(command: () -> Unit) = threadPool.execute(command)
|
||||
override fun <T> call(task: Callable<T>): T = threadPool.submit(task).get()
|
||||
override fun <T> submit(task: () -> T): NioThreadTaskFuture<T> = ThreadPoolTaskFuture(threadPool.submit(task))
|
||||
|
||||
override fun close() {
|
||||
closed = true
|
||||
threadPool.shutdown()
|
||||
}
|
||||
|
||||
class ThreadPoolTaskFuture<T>(val future: Future<T>) : NioThreadTaskFuture<T> {
|
||||
override fun get(): T = future.get()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "SingleThreadNioThread($threadName)"
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
package cn.tursom.niothread
|
||||
|
||||
import cn.tursom.core.NonLockLinkedList
|
||||
import java.nio.channels.SelectionKey
|
||||
import java.nio.channels.Selector
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
||||
class WorkerLoopNioThread(
|
||||
val threadName: String = "nioLoopThread",
|
||||
override val selector: Selector = Selector.open(),
|
||||
override val daemon: Boolean = false,
|
||||
override val timeout: Long = 3000,
|
||||
override val workLoop: (thread: NioThread, key: SelectionKey) -> Unit
|
||||
) : NioThread {
|
||||
override var closed: Boolean = false
|
||||
|
||||
val waitQueue = NonLockLinkedList<() -> Unit>()
|
||||
//val taskQueue = LinkedBlockingDeque<Future<Any?>>()
|
||||
|
||||
override val thread = Thread {
|
||||
while (!closed) {
|
||||
try {
|
||||
if (selector.isOpen) {
|
||||
if (selector.select(timeout) != 0) {
|
||||
val keyIter = selector.selectedKeys().iterator()
|
||||
while (keyIter.hasNext()) {
|
||||
val key = keyIter.next()
|
||||
keyIter.remove()
|
||||
workLoop(this, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
while (true) try {
|
||||
(waitQueue.take() ?: break)()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
thread.name = threadName
|
||||
thread.isDaemon = daemon
|
||||
thread.start()
|
||||
}
|
||||
|
||||
override fun execute(command: () -> Unit) {
|
||||
waitQueue.add(command)
|
||||
}
|
||||
|
||||
override fun <T> submit(task: () -> T): NioThreadTaskFuture<T> {
|
||||
val f = Future<T>()
|
||||
waitQueue {
|
||||
try {
|
||||
f.resume(task())
|
||||
} catch (e: Throwable) {
|
||||
f.resumeWithException(e)
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
closed = true
|
||||
}
|
||||
|
||||
override fun wakeup() {
|
||||
if (Thread.currentThread() != thread) {
|
||||
selector.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
class Future<T> : NioThreadTaskFuture<T> {
|
||||
private val lock = Object()
|
||||
private var exception: Throwable? = null
|
||||
private var result: T? = null
|
||||
|
||||
@Throws(Throwable::class)
|
||||
override fun get(): T {
|
||||
val result = this.result
|
||||
return when {
|
||||
exception != null -> throw exception as Throwable
|
||||
result != null -> result
|
||||
else -> synchronized(lock) {
|
||||
lock.wait()
|
||||
val exception = this.exception
|
||||
if (exception != null) {
|
||||
throw exception
|
||||
} else {
|
||||
this.result!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resume(value: T) {
|
||||
result = value
|
||||
synchronized(lock) {
|
||||
lock.notifyAll()
|
||||
}
|
||||
}
|
||||
|
||||
fun resumeWithException(e: Throwable) {
|
||||
exception = e
|
||||
synchronized(lock) {
|
||||
lock.notifyAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package cn.tursom.niothread.loophandler
|
||||
|
||||
import cn.tursom.niothread.NioThread
|
||||
import cn.tursom.socket.NioProtocol
|
||||
import java.nio.channels.SelectionKey
|
||||
import java.nio.channels.ServerSocketChannel
|
||||
|
||||
class BossLoopHandler(private val protocol: NioProtocol, private val workerThread: NioThread? = null) {
|
||||
fun handle(nioThread: NioThread, key: SelectionKey) {
|
||||
val workerThread: NioThread = workerThread ?: nioThread
|
||||
try {
|
||||
when {
|
||||
key.isAcceptable -> {
|
||||
val serverChannel = key.channel() as ServerSocketChannel
|
||||
while (true) {
|
||||
val channel = serverChannel.accept() ?: break
|
||||
channel.configureBlocking(false)
|
||||
workerThread.register(channel, 0) {
|
||||
protocol.handleConnect(it, workerThread)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
try {
|
||||
protocol.exceptionCause(key, nioThread, e)
|
||||
} catch (e1: Throwable) {
|
||||
e.printStackTrace()
|
||||
e1.printStackTrace()
|
||||
key.cancel()
|
||||
key.channel().close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.tursom.niothread.loophandler
|
||||
|
||||
import cn.tursom.socket.NioProtocol
|
||||
import cn.tursom.niothread.NioThread
|
||||
import java.nio.channels.SelectionKey
|
||||
|
||||
class WorkerLoopHandler(private val protocol: NioProtocol) {
|
||||
fun handle(nioThread: NioThread, key: SelectionKey) {
|
||||
try {
|
||||
when {
|
||||
key.isReadable -> {
|
||||
protocol.handleRead(key, nioThread)
|
||||
}
|
||||
key.isWritable -> {
|
||||
protocol.handleWrite(key, nioThread)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
try {
|
||||
protocol.exceptionCause(key, nioThread, e)
|
||||
} catch (e1: Throwable) {
|
||||
e.printStackTrace()
|
||||
e1.printStackTrace()
|
||||
key.cancel()
|
||||
key.channel().close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
AsyncSocket/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt
Normal file
87
AsyncSocket/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt
Normal file
@ -0,0 +1,87 @@
|
||||
package cn.tursom.socket
|
||||
|
||||
import cn.tursom.buffer.MultipleByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.pool.MemoryPool
|
||||
import cn.tursom.niothread.NioThread
|
||||
import java.io.Closeable
|
||||
import java.net.SocketException
|
||||
import java.nio.channels.SelectionKey
|
||||
import java.nio.channels.SocketChannel
|
||||
|
||||
interface AsyncSocket : Closeable {
|
||||
val open: Boolean
|
||||
val channel: SocketChannel
|
||||
val key: SelectionKey
|
||||
val nioThread: NioThread
|
||||
|
||||
suspend fun write(buffer: Array<out ByteBuffer>, timeout: Long = 0L): Long
|
||||
suspend fun read(buffer: Array<out ByteBuffer>, timeout: Long = 0L): Long
|
||||
suspend fun write(buffer: ByteBuffer, timeout: Long = 0L): Int = write(arrayOf(buffer), timeout).toInt()
|
||||
suspend fun read(buffer: ByteBuffer, timeout: Long = 0L): Int = read(arrayOf(buffer), timeout).toInt()
|
||||
suspend fun write(buffer: MultipleByteBuffer, timeout: Long = 0L): Long = write(buffer.buffers, timeout)
|
||||
suspend fun read(buffer: MultipleByteBuffer, timeout: Long = 0L): Long = read(buffer.buffers, timeout)
|
||||
|
||||
/**
|
||||
* 在有数据读取的时候自动由内存池分配内存
|
||||
*/
|
||||
@Throws(SocketException::class)
|
||||
suspend fun read(pool: MemoryPool, timeout: Long = 0L): ByteBuffer
|
||||
|
||||
override fun close()
|
||||
|
||||
fun waitMode() {
|
||||
if (Thread.currentThread() == nioThread.thread) {
|
||||
if (key.isValid) key.interestOps(SelectionKey.OP_WRITE)
|
||||
} else {
|
||||
nioThread.execute { if (key.isValid) key.interestOps(0) }
|
||||
nioThread.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
fun readMode() {
|
||||
if (Thread.currentThread() == nioThread.thread) {
|
||||
if (key.isValid) key.interestOps(SelectionKey.OP_WRITE)
|
||||
} else {
|
||||
nioThread.execute {
|
||||
if (key.isValid) key.interestOps(SelectionKey.OP_READ)
|
||||
}
|
||||
nioThread.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
fun writeMode() {
|
||||
if (Thread.currentThread() == nioThread.thread) {
|
||||
if (key.isValid) key.interestOps(SelectionKey.OP_WRITE)
|
||||
} else {
|
||||
nioThread.execute { if (key.isValid) key.interestOps(SelectionKey.OP_WRITE) }
|
||||
nioThread.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果通道已断开则会抛出异常
|
||||
*/
|
||||
suspend fun recv(buffer: ByteBuffer, timeout: Long = 0): Int {
|
||||
if (buffer.writeable == 0) return emptyBufferCode
|
||||
val readSize = read(buffer, timeout)
|
||||
if (readSize < 0) {
|
||||
throw SocketException("channel closed")
|
||||
}
|
||||
return readSize
|
||||
}
|
||||
|
||||
suspend fun recv(buffers: Array<out ByteBuffer>, timeout: Long = 0): Long {
|
||||
if (buffers.isEmpty()) return emptyBufferLongCode
|
||||
val readSize = read(buffers, timeout)
|
||||
if (readSize < 0) {
|
||||
throw SocketException("channel closed")
|
||||
}
|
||||
return readSize
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val emptyBufferCode = 0
|
||||
const val emptyBufferLongCode = 0L
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package cn.tursom.socket
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.pool.MemoryPool
|
||||
|
||||
interface BufferedAsyncSocket : AsyncSocket {
|
||||
val pool: MemoryPool
|
||||
suspend fun read(timeout: Long = 0L): ByteBuffer = read(pool, timeout)
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package cn.tursom.socket
|
||||
|
||||
import cn.tursom.core.pool.MemoryPool
|
||||
|
||||
class BufferedNioSocket(
|
||||
val socket: AsyncSocket,
|
||||
override val pool: MemoryPool
|
||||
) : BufferedAsyncSocket, AsyncSocket by socket
|
51
AsyncSocket/src/main/kotlin/cn/tursom/socket/NioClient.kt
Normal file
51
AsyncSocket/src/main/kotlin/cn/tursom/socket/NioClient.kt
Normal file
@ -0,0 +1,51 @@
|
||||
package cn.tursom.socket
|
||||
|
||||
import cn.tursom.niothread.WorkerLoopNioThread
|
||||
import cn.tursom.niothread.loophandler.WorkerLoopHandler
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.SocketException
|
||||
import java.nio.channels.SelectableChannel
|
||||
import java.nio.channels.SelectionKey
|
||||
import java.nio.channels.SocketChannel
|
||||
import java.util.concurrent.TimeoutException
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
object NioClient {
|
||||
private const val TIMEOUT = 1000L
|
||||
private val protocol = NioSocket.nioSocketProtocol
|
||||
@JvmStatic
|
||||
private val nioThread = WorkerLoopNioThread(
|
||||
"nioClient",
|
||||
daemon = true,
|
||||
workLoop = WorkerLoopHandler(protocol)::handle
|
||||
)
|
||||
|
||||
suspend fun connect(host: String, port: Int, timeout: Long = 0): NioSocket {
|
||||
val key: SelectionKey = suspendCoroutine { cont ->
|
||||
val channel = getConnection(host, port)
|
||||
val timeoutTask = if (timeout > 0) NioSocket.timer.exec(timeout) {
|
||||
channel.close()
|
||||
cont.resumeWithException(TimeoutException())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
nioThread.register(channel, 0) { key ->
|
||||
timeoutTask?.cancel()
|
||||
cont.resume(key)
|
||||
}
|
||||
}
|
||||
return NioSocket(key, nioThread)
|
||||
}
|
||||
|
||||
private fun getConnection(host: String, port: Int): SelectableChannel {
|
||||
val channel = SocketChannel.open()!!
|
||||
if (!channel.connect(InetSocketAddress(host, port))) {
|
||||
throw SocketException("connection failed")
|
||||
}
|
||||
channel.configureBlocking(false)
|
||||
return channel
|
||||
}
|
||||
}
|
23
AsyncSocket/src/main/kotlin/cn/tursom/socket/NioProtocol.kt
Normal file
23
AsyncSocket/src/main/kotlin/cn/tursom/socket/NioProtocol.kt
Normal file
@ -0,0 +1,23 @@
|
||||
package cn.tursom.socket
|
||||
|
||||
import cn.tursom.niothread.NioThread
|
||||
import java.nio.channels.SelectionKey
|
||||
|
||||
interface NioProtocol {
|
||||
@Throws(Throwable::class)
|
||||
fun handleConnect(key: SelectionKey, nioThread: NioThread) {
|
||||
}
|
||||
|
||||
@Throws(Throwable::class)
|
||||
fun handleRead(key: SelectionKey, nioThread: NioThread)
|
||||
|
||||
@Throws(Throwable::class)
|
||||
fun handleWrite(key: SelectionKey, nioThread: NioThread)
|
||||
|
||||
@Throws(Throwable::class)
|
||||
fun exceptionCause(key: SelectionKey, nioThread: NioThread, e: Throwable) {
|
||||
key.cancel()
|
||||
key.channel().close()
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
163
AsyncSocket/src/main/kotlin/cn/tursom/socket/NioSocket.kt
Normal file
163
AsyncSocket/src/main/kotlin/cn/tursom/socket/NioSocket.kt
Normal file
@ -0,0 +1,163 @@
|
||||
package cn.tursom.socket
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.read
|
||||
import cn.tursom.core.buffer.write
|
||||
import cn.tursom.core.pool.MemoryPool
|
||||
import cn.tursom.core.timer.Timer
|
||||
import cn.tursom.core.timer.TimerTask
|
||||
import cn.tursom.core.timer.WheelTimer
|
||||
import cn.tursom.niothread.NioThread
|
||||
import java.net.SocketException
|
||||
import java.nio.channels.SelectionKey
|
||||
import java.nio.channels.SocketChannel
|
||||
import java.util.concurrent.TimeoutException
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
/**
|
||||
* 异步协程套接字对象
|
||||
*/
|
||||
class NioSocket(override val key: SelectionKey, override val nioThread: NioThread) : AsyncSocket {
|
||||
override val channel: SocketChannel = key.channel() as SocketChannel
|
||||
override val open: Boolean get() = channel.isOpen && key.isValid
|
||||
|
||||
override suspend fun read(buffer: ByteBuffer, timeout: Long): Int {
|
||||
if (buffer.writeable == 0) return emptyBufferCode
|
||||
return operate {
|
||||
waitRead(timeout)
|
||||
channel.read(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun read(buffer: Array<out ByteBuffer>, timeout: Long): Long {
|
||||
if (buffer.isEmpty() && buffer.all { it.writeable != 0 }) return emptyBufferLongCode
|
||||
return operate {
|
||||
waitRead(timeout)
|
||||
channel.read(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun write(buffer: ByteBuffer, timeout: Long): Int {
|
||||
if (buffer.readable == 0) return emptyBufferCode
|
||||
return operate {
|
||||
waitWrite(timeout)
|
||||
channel.write(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun write(buffer: Array<out ByteBuffer>, timeout: Long): Long {
|
||||
if (buffer.isEmpty() && buffer.all { it.readable != 0 }) return emptyBufferLongCode
|
||||
return operate {
|
||||
waitWrite(timeout)
|
||||
channel.write(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun read(pool: MemoryPool, timeout: Long): ByteBuffer = operate {
|
||||
waitRead(timeout)
|
||||
val buffer = pool.get()
|
||||
if (channel.read(buffer) < 0) throw SocketException()
|
||||
buffer
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (channel.isOpen || key.isValid) {
|
||||
nioThread.execute {
|
||||
channel.close()
|
||||
key.cancel()
|
||||
}
|
||||
nioThread.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> operate(action: () -> T): T {
|
||||
return try {
|
||||
action()
|
||||
} catch (e: Exception) {
|
||||
waitMode()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun waitRead(timeout: Long = 0) {
|
||||
suspendCoroutine<Int> {
|
||||
key.attach(Context(it, if (timeout > 0) timer.exec(timeout) {
|
||||
key.attach(null)
|
||||
waitMode()
|
||||
it.resumeWithException(TimeoutException())
|
||||
} else null))
|
||||
readMode()
|
||||
nioThread.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun waitWrite(timeout: Long = 0) {
|
||||
suspendCoroutine<Int> {
|
||||
key.attach(Context(it, if (timeout > 0) timer.exec(timeout) {
|
||||
key.attach(null)
|
||||
waitMode()
|
||||
it.resumeWithException(TimeoutException())
|
||||
} else null))
|
||||
writeMode()
|
||||
nioThread.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
data class Context(val cont: Continuation<Int>, val timeoutTask: TimerTask? = null)
|
||||
data class ConnectContext(val cont: Continuation<SelectionKey>, val timeoutTask: TimerTask? = null)
|
||||
|
||||
protected fun finalize() {
|
||||
close()
|
||||
}
|
||||
|
||||
/**
|
||||
* 伴生对象
|
||||
*/
|
||||
companion object {
|
||||
|
||||
val nioSocketProtocol = object : NioProtocol {
|
||||
override fun handleConnect(key: SelectionKey, nioThread: NioThread) {
|
||||
key.interestOps(0)
|
||||
val context = key.attachment() as ConnectContext? ?: return
|
||||
context.timeoutTask?.cancel()
|
||||
context.cont.resume(key)
|
||||
}
|
||||
|
||||
override fun handleRead(key: SelectionKey, nioThread: NioThread) {
|
||||
key.interestOps(0)
|
||||
//logE("read ready")
|
||||
val context = key.attachment() as Context? ?: return
|
||||
context.timeoutTask?.cancel()
|
||||
context.cont.resume(0)
|
||||
}
|
||||
|
||||
override fun handleWrite(key: SelectionKey, nioThread: NioThread) {
|
||||
key.interestOps(0)
|
||||
val context = key.attachment() as Context? ?: return
|
||||
context.timeoutTask?.cancel()
|
||||
context.cont.resume(0)
|
||||
}
|
||||
|
||||
override fun exceptionCause(key: SelectionKey, nioThread: NioThread, e: Throwable) {
|
||||
key.interestOps(0)
|
||||
val context = key.attachment() as Context?
|
||||
if (context != null)
|
||||
context.cont.resumeWithException(e)
|
||||
else {
|
||||
key.cancel()
|
||||
key.channel().close()
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//val timer = StaticWheelTimer.timer
|
||||
val timer: Timer = WheelTimer.timer
|
||||
|
||||
const val emptyBufferCode = 0
|
||||
const val emptyBufferLongCode = 0L
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package cn.tursom.socket.server
|
||||
|
||||
import cn.tursom.core.pool.DirectMemoryPool
|
||||
import cn.tursom.core.pool.ExpandableMemoryPool
|
||||
import cn.tursom.core.pool.MarkedMemoryPool
|
||||
import cn.tursom.core.pool.MemoryPool
|
||||
import cn.tursom.socket.BufferedAsyncSocket
|
||||
import cn.tursom.socket.BufferedNioSocket
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
||||
/**
|
||||
* 带内存池的 NIO 套接字服务器。
|
||||
* 在处理结束后会自动释放由内存池分配的内存
|
||||
*/
|
||||
class BuffedNioServer(
|
||||
port: Int,
|
||||
private val memoryPool: MemoryPool,
|
||||
backlog: Int = 50,
|
||||
coroutineScope: CoroutineScope = GlobalScope,
|
||||
handler: suspend BufferedAsyncSocket.() -> Unit
|
||||
) : NioServer(port, backlog, coroutineScope, {
|
||||
MarkedMemoryPool(memoryPool).use { marked ->
|
||||
BufferedNioSocket(this, marked).handler()
|
||||
}
|
||||
}) {
|
||||
constructor(
|
||||
port: Int,
|
||||
blockSize: Int = 1024,
|
||||
blockCount: Int = 128,
|
||||
backlog: Int = 50,
|
||||
coroutineScope: CoroutineScope = GlobalScope,
|
||||
handler: suspend BufferedAsyncSocket.() -> Unit
|
||||
) : this(
|
||||
port,
|
||||
ExpandableMemoryPool { DirectMemoryPool(blockSize, blockCount) },
|
||||
backlog,
|
||||
coroutineScope,
|
||||
handler
|
||||
)
|
||||
|
||||
override fun close() {
|
||||
super.close()
|
||||
memoryPool.gc()
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package cn.tursom.socket.server
|
||||
|
||||
import cn.tursom.niothread.WorkerLoopNioThread
|
||||
import cn.tursom.niothread.loophandler.BossLoopHandler
|
||||
import cn.tursom.socket.NioProtocol
|
||||
import cn.tursom.niothread.loophandler.WorkerLoopHandler
|
||||
import cn.tursom.niothread.NioThread
|
||||
import java.net.InetSocketAddress
|
||||
import java.nio.channels.SelectionKey
|
||||
import java.nio.channels.ServerSocketChannel
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* 工作在单线程上的 Nio 服务器。
|
||||
*/
|
||||
class NioLoopServer(
|
||||
override val port: Int,
|
||||
private val protocol: NioProtocol,
|
||||
private val backLog: Int = 50,
|
||||
nioThreadFactory: (
|
||||
threadName: String,
|
||||
workLoop: (thread: NioThread, key: SelectionKey) -> Unit
|
||||
) -> NioThread = { name, workLoop ->
|
||||
WorkerLoopNioThread(name, workLoop = workLoop, daemon = false)
|
||||
}
|
||||
) : SocketServer {
|
||||
private val listenChannel = ServerSocketChannel.open()
|
||||
private val workerNioThread = nioThreadFactory("nio-worker", WorkerLoopHandler(
|
||||
protocol
|
||||
)::handle)
|
||||
private val bossNioThread = nioThreadFactory("nio-boss", BossLoopHandler(
|
||||
protocol,
|
||||
workerNioThread
|
||||
)::handle)
|
||||
private val started = AtomicBoolean(false)
|
||||
|
||||
override fun run() {
|
||||
if (started.compareAndSet(false, true)) {
|
||||
listenChannel.socket().bind(InetSocketAddress(port), backLog)
|
||||
listenChannel.configureBlocking(false)
|
||||
bossNioThread.register(listenChannel, SelectionKey.OP_ACCEPT) {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
listenChannel.close()
|
||||
workerNioThread.close()
|
||||
bossNioThread.close()
|
||||
}
|
||||
|
||||
protected fun finalize() {
|
||||
close()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TIMEOUT = 1000L
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package cn.tursom.socket.server
|
||||
|
||||
import cn.tursom.socket.AsyncSocket
|
||||
import cn.tursom.socket.NioSocket
|
||||
import cn.tursom.socket.NioProtocol
|
||||
import cn.tursom.niothread.NioThread
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.nio.channels.SelectionKey
|
||||
|
||||
/**
|
||||
* 只有一个管理线程的协程套接字服务器
|
||||
* 协程运行的线程是独立于管理线程的
|
||||
*/
|
||||
open class NioServer(
|
||||
override val port: Int,
|
||||
backlog: Int = 50,
|
||||
coroutineScope: CoroutineScope = GlobalScope,
|
||||
val handler: suspend AsyncSocket.() -> Unit
|
||||
) : SocketServer by NioLoopServer(port, object : NioProtocol by NioSocket.nioSocketProtocol {
|
||||
override fun handleConnect(key: SelectionKey, nioThread: NioThread) {
|
||||
coroutineScope.launch {
|
||||
val socket = NioSocket(key, nioThread)
|
||||
try {
|
||||
socket.handler()
|
||||
} catch (e: Exception) {
|
||||
Exception(e).printStackTrace()
|
||||
} finally {
|
||||
try {
|
||||
socket.close()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, backlog)
|
||||
|
@ -0,0 +1,14 @@
|
||||
package cn.tursom.socket.server
|
||||
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
* 套接字服务器的基本形式,提供运行、关闭的基本操作
|
||||
* 其应支持最基本的创建形式:
|
||||
* XXXServer(port) {
|
||||
* // 业务逻辑
|
||||
* }
|
||||
*/
|
||||
interface SocketServer : Runnable, Closeable {
|
||||
val port: Int
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
rootProject.name = 'TursomServer'
|
||||
include 'web', 'aop', 'database', 'database:database-async', 'utils', 'utils:xml', 'utils:async-http', 'web:netty-web'
|
||||
include 'socket', 'socket:socket-async'
|
||||
include 'AsyncSocket'
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package cn.tursom.socket
|
||||
|
||||
import cn.tursom.core.log
|
||||
import cn.tursom.core.logE
|
||||
import cn.tursom.core.timer.TimerTask
|
||||
import cn.tursom.core.timer.WheelTimer
|
||||
import cn.tursom.socket.niothread.INioThread
|
||||
|
@ -1,9 +1,7 @@
|
||||
package cn.tursom.socket
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.readNioBuffer
|
||||
import cn.tursom.core.bytebuffer.writeNioBuffer
|
||||
import cn.tursom.core.logE
|
||||
import cn.tursom.core.buffer.read
|
||||
import cn.tursom.core.buffer.write
|
||||
import java.io.Closeable
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
@ -13,35 +11,30 @@ interface AsyncSocket : Closeable {
|
||||
suspend fun write(buffer: ByteBuffer, timeout: Long = 0L): Int = write(arrayOf(buffer), timeout).toInt()
|
||||
suspend fun read(buffer: ByteBuffer, timeout: Long = 0L): Int = read(arrayOf(buffer), timeout).toInt()
|
||||
override fun close()
|
||||
|
||||
suspend fun write(buffer: AdvanceByteBuffer, timeout: Long = 0): Int {
|
||||
return if (buffer.bufferCount == 1) {
|
||||
buffer.readNioBuffer {
|
||||
//logE(it.toString())
|
||||
write(it, timeout)
|
||||
}
|
||||
} else {
|
||||
val readMode = buffer.readMode
|
||||
buffer.readMode()
|
||||
val value = write(buffer.nioBuffers, timeout).toInt()
|
||||
if (!readMode) buffer.resumeWriteMode()
|
||||
value
|
||||
|
||||
suspend fun write(buffer: cn.tursom.core.buffer.ByteBuffer, timeout: Long = 0): Int {
|
||||
return buffer.read {
|
||||
write(it, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun read(buffer: AdvanceByteBuffer, timeout: Long = 0): Int {
|
||||
//logE("buffer.bufferCount: ${buffer.bufferCount}")
|
||||
//logE("AsyncSocket.read(buffer: AdvanceByteBuffer, timeout: Long = 0): buffer: $buffer")
|
||||
return if (buffer.bufferCount == 1) {
|
||||
buffer.writeNioBuffer {
|
||||
read(it, timeout)
|
||||
}
|
||||
} else {
|
||||
val readMode = buffer.readMode
|
||||
buffer.resumeWriteMode()
|
||||
val value = read(buffer.nioBuffers, timeout).toInt()
|
||||
if (readMode) buffer.readMode()
|
||||
value
|
||||
|
||||
suspend fun read(buffer: cn.tursom.core.buffer.ByteBuffer, timeout: Long = 0): Int {
|
||||
return buffer.write {
|
||||
read(it, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun write(buffers: Array<out cn.tursom.core.buffer.ByteBuffer>, timeout: Long): Long {
|
||||
val nioBuffer = buffers.map { it.readBuffer() }.toTypedArray()
|
||||
val writeSize = write(nioBuffer, timeout)
|
||||
buffers.forEachIndexed { index, byteBuffer -> byteBuffer.finishRead(nioBuffer[index]) }
|
||||
return writeSize
|
||||
}
|
||||
|
||||
suspend fun write(buffers: Collection<cn.tursom.core.buffer.ByteBuffer>, timeout: Long): Long {
|
||||
val nioBuffer = buffers.map { it.readBuffer() }.toTypedArray()
|
||||
val writeSize = write(nioBuffer, timeout)
|
||||
buffers.forEachIndexed { index, byteBuffer -> byteBuffer.finishRead(nioBuffer[index]) }
|
||||
return writeSize
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
package cn.tursom.socket
|
||||
|
||||
import cn.tursom.core.buffer.write
|
||||
import cn.tursom.socket.niothread.INioThread
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.writeNioBuffer
|
||||
import cn.tursom.core.logE
|
||||
import java.net.SocketException
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.SelectionKey
|
||||
@ -13,7 +11,7 @@ interface IAsyncNioSocket : AsyncSocket {
|
||||
val channel: SocketChannel
|
||||
val key: SelectionKey
|
||||
val nioThread: INioThread
|
||||
|
||||
|
||||
fun waitMode() {
|
||||
if (Thread.currentThread() == nioThread.thread) {
|
||||
if (key.isValid) key.interestOps(SelectionKey.OP_WRITE)
|
||||
@ -22,7 +20,7 @@ interface IAsyncNioSocket : AsyncSocket {
|
||||
nioThread.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun readMode() {
|
||||
//logE("readMode()")
|
||||
if (Thread.currentThread() == nioThread.thread) {
|
||||
@ -36,7 +34,7 @@ interface IAsyncNioSocket : AsyncSocket {
|
||||
nioThread.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun writeMode() {
|
||||
if (Thread.currentThread() == nioThread.thread) {
|
||||
if (key.isValid) key.interestOps(SelectionKey.OP_WRITE)
|
||||
@ -45,7 +43,7 @@ interface IAsyncNioSocket : AsyncSocket {
|
||||
nioThread.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend fun read(buffer: ByteBuffer): Int = read(arrayOf(buffer)).toInt()
|
||||
suspend fun write(buffer: ByteBuffer): Int = write(arrayOf(buffer)).toInt()
|
||||
suspend fun read(buffer: Array<out ByteBuffer>): Long
|
||||
@ -61,7 +59,7 @@ interface IAsyncNioSocket : AsyncSocket {
|
||||
}
|
||||
return readSize
|
||||
}
|
||||
|
||||
|
||||
suspend fun recv(buffer: ByteBuffer, timeout: Long): Int {
|
||||
if (buffer.remaining() == 0) return emptyBufferCode
|
||||
val readSize = read(buffer, timeout)
|
||||
@ -70,7 +68,7 @@ interface IAsyncNioSocket : AsyncSocket {
|
||||
}
|
||||
return readSize
|
||||
}
|
||||
|
||||
|
||||
suspend fun recv(buffers: Array<out ByteBuffer>, timeout: Long): Long {
|
||||
if (buffers.isEmpty()) return emptyBufferLongCode
|
||||
val readSize = read(buffers, timeout)
|
||||
@ -79,21 +77,13 @@ interface IAsyncNioSocket : AsyncSocket {
|
||||
}
|
||||
return readSize
|
||||
}
|
||||
|
||||
suspend fun recv(buffer: AdvanceByteBuffer, timeout: Long = 0): Int {
|
||||
return if (buffer.bufferCount == 1) {
|
||||
buffer.writeNioBuffer {
|
||||
recv(it, timeout)
|
||||
}
|
||||
} else {
|
||||
val readMode = buffer.readMode
|
||||
buffer.resumeWriteMode()
|
||||
val value = recv(buffer.nioBuffers, timeout).toInt()
|
||||
if (readMode) buffer.readMode()
|
||||
value
|
||||
|
||||
suspend fun recv(buffer: cn.tursom.core.buffer.ByteBuffer, timeout: Long = 0): Int {
|
||||
return buffer.write {
|
||||
recv(it, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val emptyBufferCode = 0
|
||||
const val emptyBufferLongCode = 0L
|
||||
|
@ -1,10 +1,10 @@
|
||||
package cn.tursom.socket.enhance
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import java.io.Closeable
|
||||
|
||||
interface SocketReader<T> : Closeable {
|
||||
suspend fun get(buffer: AdvanceByteBuffer, timeout: Long = 0): T
|
||||
override fun close()
|
||||
suspend fun get(buffer: ByteBuffer, timeout: Long = 0): T
|
||||
override fun close()
|
||||
}
|
||||
|
||||
|
@ -3,5 +3,11 @@ package cn.tursom.socket.enhance
|
||||
import java.io.Closeable
|
||||
|
||||
interface SocketWriter<T> : Closeable {
|
||||
suspend fun put(value: T, timeout: Long = 0)
|
||||
suspend fun put(value: T, timeout: Long) {
|
||||
put(value)
|
||||
flush(timeout)
|
||||
}
|
||||
|
||||
suspend fun put(value: T)
|
||||
suspend fun flush(timeout: Long = 0)
|
||||
}
|
@ -1,37 +1,37 @@
|
||||
package cn.tursom.socket.enhance.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
import cn.tursom.socket.IAsyncNioSocket
|
||||
import cn.tursom.socket.enhance.SocketReader
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.ByteArrayAdvanceByteBuffer
|
||||
|
||||
class LengthFieldBasedFrameReader(
|
||||
val prevReader: SocketReader<AdvanceByteBuffer>
|
||||
) : SocketReader<AdvanceByteBuffer> {
|
||||
val prevReader: SocketReader<ByteBuffer>
|
||||
) : SocketReader<ByteBuffer> {
|
||||
constructor(socket: IAsyncNioSocket) : this(SimpSocketReader(socket))
|
||||
|
||||
override suspend fun get(buffer: AdvanceByteBuffer, timeout: Long): AdvanceByteBuffer {
|
||||
override suspend fun get(buffer: ByteBuffer, timeout: Long): ByteBuffer {
|
||||
val rBuf = prevReader.get(buffer, timeout)
|
||||
val blockSize = rBuf.getInt()
|
||||
if (rBuf.readableSize == blockSize) {
|
||||
if (rBuf.readable == blockSize) {
|
||||
return rBuf
|
||||
} else if (rBuf.readableSize > blockSize) {
|
||||
} else if (rBuf.readable > blockSize) {
|
||||
return if (rBuf.hasArray) {
|
||||
val retBuf = ByteArrayAdvanceByteBuffer(rBuf.array, rBuf.readOffset, blockSize)
|
||||
val retBuf = HeapByteBuffer(rBuf.array, rBuf.readOffset, blockSize)
|
||||
rBuf.readPosition += blockSize
|
||||
retBuf
|
||||
} else {
|
||||
val targetBuffer = ByteArrayAdvanceByteBuffer(blockSize)
|
||||
val targetBuffer = HeapByteBuffer(blockSize)
|
||||
rBuf.writeTo(targetBuffer)
|
||||
targetBuffer
|
||||
}
|
||||
}
|
||||
|
||||
val targetBuffer = ByteArrayAdvanceByteBuffer(blockSize)
|
||||
val targetBuffer = HeapByteBuffer(blockSize)
|
||||
rBuf.writeTo(targetBuffer)
|
||||
while (targetBuffer.writeableSize != 0) {
|
||||
while (targetBuffer.writeable != 0) {
|
||||
val rBuf2 = prevReader.get(buffer, timeout)
|
||||
if (rBuf2.readableSize == 0) return targetBuffer
|
||||
if (rBuf2.readable == 0) return targetBuffer
|
||||
rBuf2.writeTo(targetBuffer)
|
||||
}
|
||||
return targetBuffer
|
||||
|
@ -1,33 +1,38 @@
|
||||
package cn.tursom.socket.enhance.impl
|
||||
|
||||
import cn.tursom.buffer.MultipleByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.ArrayByteBuffer
|
||||
import cn.tursom.socket.IAsyncNioSocket
|
||||
import cn.tursom.socket.enhance.SocketWriter
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.ByteArrayAdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.MultiAdvanceByteBuffer
|
||||
import cn.tursom.core.pool.DirectMemoryPool
|
||||
import cn.tursom.core.pool.ExpandableMemoryPool
|
||||
|
||||
|
||||
class LengthFieldPrependWriter(
|
||||
val prevWriter: SocketWriter<AdvanceByteBuffer>
|
||||
) : SocketWriter<AdvanceByteBuffer> {
|
||||
constructor(socket: IAsyncNioSocket) : this(SimpSocketWriter(socket))
|
||||
val prevWriter: SocketWriter<ByteBuffer>
|
||||
) : SocketWriter<ByteBuffer> {
|
||||
constructor(socket: IAsyncNioSocket) : this(SimpSocketWriter(socket))
|
||||
|
||||
override suspend fun put(value: AdvanceByteBuffer, timeout: Long) {
|
||||
val memToken = directMemoryPool.allocate()
|
||||
val buffer = directMemoryPool.getAdvanceByteBuffer(memToken) ?: ByteArrayAdvanceByteBuffer(4)
|
||||
buffer.put(value.readableSize)
|
||||
prevWriter.put(MultiAdvanceByteBuffer(buffer, value))
|
||||
directMemoryPool.free(memToken)
|
||||
}
|
||||
override suspend fun put(value: ByteBuffer) {
|
||||
val buffer = directMemoryPool.getMemory()
|
||||
buffer.put(value.readable)
|
||||
prevWriter.put(buffer)
|
||||
prevWriter.put(value)
|
||||
buffer.close()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
prevWriter.close()
|
||||
}
|
||||
override suspend fun flush(timeout: Long) {
|
||||
prevWriter.flush()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private val directMemoryPool = DirectMemoryPool(4, 1024)
|
||||
}
|
||||
override fun close() {
|
||||
prevWriter.close()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private val directMemoryPool = ExpandableMemoryPool { DirectMemoryPool(4, 64) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
package cn.tursom.socket.enhance.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.socket.IAsyncNioSocket
|
||||
import cn.tursom.socket.enhance.SocketReader
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
|
||||
class SimpSocketReader(
|
||||
val socket: IAsyncNioSocket
|
||||
) : SocketReader<AdvanceByteBuffer> {
|
||||
override suspend fun get(buffer: AdvanceByteBuffer, timeout: Long): AdvanceByteBuffer {
|
||||
) : SocketReader<ByteBuffer> {
|
||||
override suspend fun get(buffer: ByteBuffer, timeout: Long): ByteBuffer {
|
||||
buffer.reset()
|
||||
if (socket.read(buffer) < 0) {
|
||||
socket.close()
|
||||
|
@ -1,17 +1,26 @@
|
||||
package cn.tursom.socket.enhance.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.socket.IAsyncNioSocket
|
||||
import cn.tursom.socket.enhance.SocketWriter
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
class SimpSocketWriter(
|
||||
val socket: IAsyncNioSocket
|
||||
) : SocketWriter<AdvanceByteBuffer> {
|
||||
override suspend fun put(value: AdvanceByteBuffer, timeout: Long) {
|
||||
socket.write(value, timeout)
|
||||
}
|
||||
val socket: IAsyncNioSocket
|
||||
) : SocketWriter<ByteBuffer> {
|
||||
private val bufferQueue = ConcurrentLinkedQueue<ByteBuffer>()
|
||||
override suspend fun put(value: ByteBuffer) {
|
||||
bufferQueue.offer(value)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
socket.close()
|
||||
}
|
||||
override suspend fun flush(timeout: Long) {
|
||||
val buffers = bufferQueue.toTypedArray()
|
||||
bufferQueue.clear()
|
||||
socket.write(buffers, timeout)
|
||||
buffers.forEach { it.close() }
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
socket.close()
|
||||
}
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
package cn.tursom.socket.enhance.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.socket.IAsyncNioSocket
|
||||
import cn.tursom.socket.enhance.SocketReader
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
|
||||
class StringReader(
|
||||
val prevReader: SocketReader<AdvanceByteBuffer>
|
||||
val prevReader: SocketReader<ByteBuffer>
|
||||
) : SocketReader<String> {
|
||||
constructor(socket: IAsyncNioSocket) : this(LengthFieldBasedFrameReader(socket))
|
||||
|
||||
override suspend fun get(buffer: AdvanceByteBuffer, timeout: Long): String {
|
||||
override suspend fun get(buffer: ByteBuffer, timeout: Long): String {
|
||||
return prevReader.get(buffer, timeout).getString()
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,17 @@
|
||||
package cn.tursom.socket.enhance.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.socket.IAsyncNioSocket
|
||||
import cn.tursom.socket.enhance.EnhanceSocket
|
||||
import cn.tursom.socket.enhance.SocketReader
|
||||
import cn.tursom.socket.enhance.SocketWriter
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
|
||||
class StringSocket(
|
||||
socket: IAsyncNioSocket,
|
||||
prevReader: SocketReader<AdvanceByteBuffer> = LengthFieldBasedFrameReader(socket),
|
||||
prevWriter: SocketWriter<AdvanceByteBuffer> = LengthFieldPrependWriter(socket)
|
||||
socket: IAsyncNioSocket,
|
||||
prevReader: SocketReader<ByteBuffer> = LengthFieldBasedFrameReader(socket),
|
||||
prevWriter: SocketWriter<ByteBuffer> = LengthFieldPrependWriter(socket)
|
||||
) : EnhanceSocket<String, String> by UnionEnhanceSocket(
|
||||
socket,
|
||||
StringReader(prevReader),
|
||||
StringWriter(prevWriter)
|
||||
socket,
|
||||
StringReader(prevReader),
|
||||
StringWriter(prevWriter)
|
||||
)
|
@ -1,22 +1,26 @@
|
||||
package cn.tursom.socket.enhance.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
import cn.tursom.socket.IAsyncNioSocket
|
||||
import cn.tursom.socket.enhance.SocketWriter
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.ByteArrayAdvanceByteBuffer
|
||||
|
||||
class StringWriter(
|
||||
val prevWriter: SocketWriter<AdvanceByteBuffer>
|
||||
val prevWriter: SocketWriter<ByteBuffer>
|
||||
) : SocketWriter<String> {
|
||||
constructor(socket: IAsyncNioSocket) : this(LengthFieldPrependWriter(socket))
|
||||
constructor(socket: IAsyncNioSocket) : this(LengthFieldPrependWriter(socket))
|
||||
|
||||
override suspend fun put(value: String, timeout: Long) {
|
||||
val buf = ByteArrayAdvanceByteBuffer(value.toByteArray())
|
||||
buf.writePosition = buf.limit
|
||||
prevWriter.put(buf, timeout)
|
||||
}
|
||||
override suspend fun put(value: String) {
|
||||
val buf = HeapByteBuffer(value.toByteArray())
|
||||
buf.writePosition = buf.capacity
|
||||
prevWriter.put(buf)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
prevWriter.close()
|
||||
}
|
||||
override suspend fun flush(timeout: Long) {
|
||||
prevWriter.flush(timeout)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
prevWriter.close()
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
package cn.tursom.socket.enhance.impl
|
||||
|
||||
import cn.tursom.socket.enhance.SocketReader
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.timer.TimerTask
|
||||
import cn.tursom.core.timer.WheelTimer
|
||||
import cn.tursom.socket.enhance.SocketReader
|
||||
|
||||
class TimeoutReader<Read>(val prevReader: SocketReader<Read>, val timeout: Long = 5000L) : SocketReader<Read> {
|
||||
private var timerTask: TimerTask? = null
|
||||
override suspend fun get(buffer: AdvanceByteBuffer, timeout: Long): Read {
|
||||
override suspend fun get(buffer: ByteBuffer, timeout: Long): Read {
|
||||
timerTask?.cancel()
|
||||
timerTask = timer.exec(this.timeout) {
|
||||
prevReader.close()
|
||||
|
@ -1,25 +1,23 @@
|
||||
package cn.tursom.socket.server
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.ByteArrayAdvanceByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.pool.DirectMemoryPool
|
||||
import cn.tursom.core.pool.MemoryPool
|
||||
import cn.tursom.core.pool.usingAdvanceByteBuffer
|
||||
import cn.tursom.socket.AsyncNioSocket
|
||||
|
||||
/**
|
||||
* 带内存池的 NIO 套接字服务器。<br />
|
||||
* 其构造函数是标准写法的改造,会向 handler 方法传入一个 AdvanceByteBuffer,默认是 DirectAdvanceByteBuffer,
|
||||
* 当内存池用完之后会换为 ByteArrayAdvanceByteBuffer。
|
||||
* 其构造函数是标准写法的改造,会向 handler 方法传入一个 ByteBuffer,默认是 DirectByteBuffer,
|
||||
* 当内存池用完之后会换为 ByteArrayByteBuffer。
|
||||
*/
|
||||
class BuffedAsyncNioServer(
|
||||
port: Int,
|
||||
memoryPool: MemoryPool,
|
||||
backlog: Int = 50,
|
||||
handler: suspend AsyncNioSocket.(buffer: AdvanceByteBuffer) -> Unit
|
||||
handler: suspend AsyncNioSocket.(buffer: ByteBuffer) -> Unit
|
||||
) : IAsyncNioServer by AsyncNioServer(port, backlog, {
|
||||
memoryPool.usingAdvanceByteBuffer {
|
||||
handler(it ?: ByteArrayAdvanceByteBuffer(memoryPool.blockSize))
|
||||
memoryPool {
|
||||
handler(it)
|
||||
}
|
||||
}) {
|
||||
constructor(
|
||||
@ -27,6 +25,6 @@ class BuffedAsyncNioServer(
|
||||
blockSize: Int = 1024,
|
||||
blockCount: Int = 128,
|
||||
backlog: Int = 50,
|
||||
handler: suspend AsyncNioSocket.(buffer: AdvanceByteBuffer) -> Unit
|
||||
handler: suspend AsyncNioSocket.(buffer: ByteBuffer) -> Unit
|
||||
) : this(port, DirectMemoryPool(blockSize, blockCount), backlog, handler)
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
import cn.tursom.core.bytebuffer.ByteArrayAdvanceByteBuffer
|
||||
import cn.tursom.core.pool.DirectMemoryPool
|
||||
import cn.tursom.core.pool.usingAdvanceByteBuffer
|
||||
import cn.tursom.socket.server.AsyncNioServer
|
||||
|
||||
fun main() {
|
||||
// 服务器端口,可任意指定
|
||||
val port = 12345
|
||||
|
||||
// 创建一个直接内存池,每个块是1024字节,共有256个快
|
||||
val memoryPool = DirectMemoryPool(1024, 256)
|
||||
// 创建服务器对象
|
||||
val server = AsyncNioServer(port) {
|
||||
// 这里处理业务逻辑,套接字对象被以 this 的方式传进来
|
||||
// 从内存池中获取一个内存块
|
||||
memoryPool.usingAdvanceByteBuffer {
|
||||
// 检查是否获取成功,不成功就创建一个堆缓冲
|
||||
val buffer = it ?: ByteArrayAdvanceByteBuffer(1024)
|
||||
// 从套接字中读数据,五秒之内没有数据就抛出异常
|
||||
read(buffer, 5000)
|
||||
// 输出读取到的数据
|
||||
println("${System.currentTimeMillis()}: recv from ${channel.remoteAddress}: ${buffer.toString(buffer.readableSize)}")
|
||||
// 原封不动的返回数据
|
||||
write(buffer)
|
||||
// 代码块结束后,框架会自动释放连接
|
||||
}
|
||||
}
|
||||
// 创建一个新线程去启动服务器
|
||||
Thread(server, "echoServerStarter").start()
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
import cn.tursom.core.bytebuffer.ByteArrayAdvanceByteBuffer
|
||||
import cn.tursom.core.log
|
||||
import cn.tursom.core.pool.DirectMemoryPool
|
||||
import cn.tursom.core.pool.usingAdvanceByteBuffer
|
||||
import cn.tursom.socket.AsyncNioClient
|
||||
import cn.tursom.socket.server.AsyncNioServer
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.lang.Thread.sleep
|
||||
import java.net.SocketException
|
||||
import java.util.concurrent.TimeoutException
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
fun main() {
|
||||
// 服务器端口,可任意指定
|
||||
val port = 12345
|
||||
|
||||
// 创建一个直接内存池,每个块是1024字节,共有256个快
|
||||
val memoryPool = DirectMemoryPool(1024, 256)
|
||||
// 创建服务器对象
|
||||
val server = AsyncNioServer(port) {
|
||||
// 这里处理业务逻辑,套接字对象被以 this 的方式传进来
|
||||
// 从内存池中获取一个内存块
|
||||
memoryPool.usingAdvanceByteBuffer {
|
||||
// 检查是否获取成功,不成功就创建一个堆缓冲
|
||||
val buffer = it ?: ByteArrayAdvanceByteBuffer(1024)
|
||||
try {
|
||||
while (true) {
|
||||
buffer.clear()
|
||||
// 从套接字中读数据,五秒之内没有数据就抛出异常
|
||||
if (read(buffer, 10_000) < 0) {
|
||||
return@AsyncNioServer
|
||||
}
|
||||
// 输出读取到的数据
|
||||
//log("server recv from ${channel.remoteAddress}: [${buffer.readableSize}] ${buffer.toString(buffer.readableSize)}")
|
||||
// 原封不动的返回数据
|
||||
val writeSize = write(buffer)
|
||||
//log("server send [$writeSize] bytes")
|
||||
}
|
||||
} catch (e: TimeoutException) {
|
||||
}
|
||||
// 代码块结束后,框架会自动释放连接
|
||||
}
|
||||
}
|
||||
server.run()
|
||||
|
||||
val connectionCount = 300
|
||||
val dataPerConn = 10
|
||||
val testData = "testData".toByteArray()
|
||||
|
||||
val remain = AtomicInteger(connectionCount)
|
||||
|
||||
val clientMemoryPool = DirectMemoryPool(1024, connectionCount)
|
||||
|
||||
val start = System.currentTimeMillis()
|
||||
|
||||
repeat(connectionCount) {
|
||||
GlobalScope.launch {
|
||||
val socket = AsyncNioClient.connect("127.0.0.1", port)
|
||||
clientMemoryPool.usingAdvanceByteBuffer {
|
||||
// 检查是否获取成功,不成功就创建一个堆缓冲
|
||||
val buffer = it ?: ByteArrayAdvanceByteBuffer(1024)
|
||||
try {
|
||||
repeat(dataPerConn) {
|
||||
buffer.clear()
|
||||
buffer.put(testData)
|
||||
//log("client sending: [${buffer.readableSize}] ${buffer.toString(buffer.readableSize)}")
|
||||
val writeSize = socket.write(buffer)
|
||||
//log("client write [$writeSize] bytes")
|
||||
//log(buffer.toString())
|
||||
val readSize = socket.read(buffer)
|
||||
//log(buffer.toString())
|
||||
//log("client recv: [$readSize:${buffer.readableSize}] ${buffer.toString(buffer.readableSize)}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Exception(e).printStackTrace()
|
||||
} finally {
|
||||
socket.close()
|
||||
}
|
||||
}
|
||||
remain.decrementAndGet()
|
||||
}
|
||||
}
|
||||
|
||||
while (remain.get() != 0) {
|
||||
println(remain.get())
|
||||
sleep(500)
|
||||
}
|
||||
|
||||
val end = System.currentTimeMillis()
|
||||
println(end - start)
|
||||
server.close()
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package cn.tursom.socket.server
|
||||
|
||||
import cn.tursom.core.bytebuffer.ByteArrayAdvanceByteBuffer
|
||||
import cn.tursom.core.log
|
||||
import cn.tursom.core.logE
|
||||
import cn.tursom.socket.AsyncAioClient
|
||||
import cn.tursom.socket.SocketClient
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
|
||||
class AsyncNioServerTest {
|
||||
private val testMsg = "hello"
|
||||
private val port = 12345
|
||||
private val server = AsyncNioServer(port) {
|
||||
log("new connection")
|
||||
val buffer = ByteArrayAdvanceByteBuffer(1024)
|
||||
while (true) {
|
||||
buffer.clear()
|
||||
read(buffer, 5000)
|
||||
logE("server recv: ${buffer.toString(buffer.readableSize)}")
|
||||
write(buffer, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
server.run()
|
||||
}
|
||||
|
||||
//@Test
|
||||
fun testAsyncNioServer() {
|
||||
runBlocking {
|
||||
val client = AsyncAioClient.connect("127.0.0.1", port)
|
||||
log("connect to server")
|
||||
val buffer = ByteArrayAdvanceByteBuffer(1024)
|
||||
repeat(10) {
|
||||
buffer.clear()
|
||||
buffer.put(testMsg)
|
||||
client.write(buffer, 5000)
|
||||
buffer.clear()
|
||||
client.read(buffer, 5000)
|
||||
log("server recv: ${buffer.getString()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//@Test
|
||||
fun testAsyncNioServerSocket() {
|
||||
SocketClient("localhost", port) {
|
||||
val buffer = ByteArray(1024)
|
||||
repeat(10) {
|
||||
send(testMsg)
|
||||
val readSize = inputStream.read(buffer)
|
||||
val recv = String(buffer, 0, readSize)
|
||||
log(recv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
package cn.tursom.socket.niothread
|
||||
|
||||
import cn.tursom.core.timer.WheelTimer
|
||||
import java.nio.channels.Selector
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.LinkedBlockingDeque
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
||||
class WorkerLoopNioThread(
|
||||
|
@ -1,66 +0,0 @@
|
||||
package cn.tursom.socket.server
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.ByteArrayAdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.readNioBuffer
|
||||
import cn.tursom.core.bytebuffer.writeNioBuffer
|
||||
import cn.tursom.core.pool.DirectMemoryPool
|
||||
import cn.tursom.core.pool.MemoryPool
|
||||
import cn.tursom.socket.INioProtocol
|
||||
import cn.tursom.socket.SocketClient
|
||||
import cn.tursom.socket.niothread.INioThread
|
||||
import org.junit.Test
|
||||
import java.nio.channels.SelectionKey
|
||||
import java.nio.channels.SocketChannel
|
||||
|
||||
class NioServerTest {
|
||||
private val port = 12345
|
||||
@Test
|
||||
fun testNioServer() {
|
||||
val memoryPool: MemoryPool = DirectMemoryPool(1024, 256)
|
||||
val server = NioServer(port, object : INioProtocol {
|
||||
override fun handleConnect(key: SelectionKey, nioThread: INioThread) {
|
||||
val memoryToken = memoryPool.allocate()
|
||||
key.attach(memoryToken to (memoryPool.getAdvanceByteBuffer(memoryToken) ?: ByteArrayAdvanceByteBuffer(1024)))
|
||||
key.interestOps(SelectionKey.OP_READ)
|
||||
}
|
||||
|
||||
override fun handleRead(key: SelectionKey, nioThread: INioThread) {
|
||||
val channel = key.channel() as SocketChannel
|
||||
val buffer = (key.attachment() as Pair<Int, AdvanceByteBuffer>).second
|
||||
buffer.writeNioBuffer {
|
||||
channel.read(it)
|
||||
}
|
||||
println("record from client: ${buffer.toString(buffer.readableSize)}")
|
||||
key.interestOps(SelectionKey.OP_WRITE)
|
||||
}
|
||||
|
||||
override fun handleWrite(key: SelectionKey, nioThread: INioThread) {
|
||||
val channel = key.channel() as SocketChannel
|
||||
val buffer = (key.attachment() as Pair<Int, AdvanceByteBuffer>).second
|
||||
println("send to client: ${buffer.toString(buffer.readableSize)}")
|
||||
buffer.readNioBuffer {
|
||||
channel.write(it)
|
||||
}
|
||||
buffer.reset()
|
||||
key.interestOps(SelectionKey.OP_READ)
|
||||
}
|
||||
|
||||
override fun exceptionCause(key: SelectionKey, nioThread: INioThread, e: Throwable) {
|
||||
super.exceptionCause(key, nioThread, e)
|
||||
val memoryToken = (key.attachment() as Pair<Int, AdvanceByteBuffer>).first
|
||||
memoryPool.free(memoryToken)
|
||||
key.channel().close()
|
||||
key.cancel()
|
||||
}
|
||||
})
|
||||
|
||||
server.run()
|
||||
val socket = SocketClient("127.0.0.1", port)
|
||||
val buffer = ByteArray(1024)
|
||||
socket.outputStream.write("Hello".toByteArray())
|
||||
val readCount = socket.inputStream.read(buffer)
|
||||
println(buffer.copyOfRange(0, readCount).toString(Charsets.UTF_8))
|
||||
server.close()
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
package cn.tursom.core
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.readNioBuffer
|
||||
import cn.tursom.core.bytebuffer.writeNioBuffer
|
||||
import java.nio.ByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.read
|
||||
import cn.tursom.core.buffer.write
|
||||
import java.nio.channels.AsynchronousFileChannel
|
||||
import java.nio.channels.CompletionHandler
|
||||
import java.nio.file.Files
|
||||
@ -18,87 +17,73 @@ import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class AsyncFile(val path: Path) {
|
||||
constructor(path: String) : this(Paths.get(path))
|
||||
constructor(path: String) : this(Paths.get(path))
|
||||
|
||||
private var existsCache = false
|
||||
private var existsCache = false
|
||||
|
||||
val exists: Boolean
|
||||
get() {
|
||||
val exists = Files.exists(path)
|
||||
existsCache = exists
|
||||
return exists
|
||||
}
|
||||
val size get() = if (existsCache || exists) Files.size(path) else 0
|
||||
val exists: Boolean
|
||||
get() {
|
||||
val exists = Files.exists(path)
|
||||
existsCache = exists
|
||||
return exists
|
||||
}
|
||||
val size get() = if (existsCache || exists) Files.size(path) else 0
|
||||
|
||||
val writeChannel: AsynchronousFileChannel by lazy { AsynchronousFileChannel.open(path, StandardOpenOption.WRITE) }
|
||||
val readChannel: AsynchronousFileChannel by lazy { AsynchronousFileChannel.open(path, StandardOpenOption.READ) }
|
||||
val writeChannel: AsynchronousFileChannel by lazy { AsynchronousFileChannel.open(path, StandardOpenOption.WRITE) }
|
||||
val readChannel: AsynchronousFileChannel by lazy { AsynchronousFileChannel.open(path, StandardOpenOption.READ) }
|
||||
|
||||
fun write(buffer: ByteBuffer, position: Long = 0) {
|
||||
create()
|
||||
writeChannel.write(buffer, position)
|
||||
}
|
||||
fun write(buffer: ByteBuffer, position: Long = 0) {
|
||||
create()
|
||||
buffer.read { writeChannel.write(it, position) }
|
||||
}
|
||||
|
||||
suspend fun writeAndWait(buffer: ByteBuffer, position: Long = 0): Int {
|
||||
create()
|
||||
return suspendCoroutine {
|
||||
writeChannel.write(buffer, position, it, handler)
|
||||
}
|
||||
}
|
||||
suspend fun writeAndWait(buffer: ByteBuffer, position: Long = 0): Int {
|
||||
create()
|
||||
return suspendCoroutine { cont ->
|
||||
buffer.read { writeChannel.write(it, position, cont, handler) }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun write(buffer: AdvanceByteBuffer, position: Long = 0): Int {
|
||||
return buffer.readNioBuffer { writeAndWait(it, position) }
|
||||
}
|
||||
fun append(buffer: ByteBuffer, position: Long = size) {
|
||||
write(buffer, position)
|
||||
}
|
||||
|
||||
fun append(buffer: ByteBuffer, position: Long = size) {
|
||||
write(buffer, position)
|
||||
}
|
||||
suspend fun appendAndWait(buffer: ByteBuffer, position: Long = size): Int {
|
||||
return writeAndWait(buffer, position)
|
||||
}
|
||||
|
||||
suspend fun appendAndWait(buffer: ByteBuffer, position: Long = size): Int {
|
||||
return writeAndWait(buffer, position)
|
||||
}
|
||||
suspend fun read(buffer: ByteBuffer, position: Long = 0): Int {
|
||||
return suspendCoroutine { cont ->
|
||||
buffer.write { readChannel.read(it, position, cont, handler) }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun append(buffer: AdvanceByteBuffer, position: Long = size): Int {
|
||||
return buffer.readNioBuffer { appendAndWait(it, position) }
|
||||
}
|
||||
fun create() = if (existsCache || !exists) {
|
||||
Files.createFile(path)
|
||||
existsCache = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
suspend fun read(buffer: ByteBuffer, position: Long = 0): Int {
|
||||
return suspendCoroutine {
|
||||
readChannel.read(buffer, position, it, handler)
|
||||
}
|
||||
}
|
||||
fun delete(): Boolean {
|
||||
existsCache = false
|
||||
return Files.deleteIfExists(path)
|
||||
}
|
||||
|
||||
suspend fun read(buffer: AdvanceByteBuffer, position: Long = 0): Int {
|
||||
return buffer.writeNioBuffer {
|
||||
read(it, position)
|
||||
}
|
||||
}
|
||||
fun close() {
|
||||
try {
|
||||
writeChannel.close()
|
||||
readChannel.close()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
fun create() = if (existsCache || !exists) {
|
||||
Files.createFile(path)
|
||||
existsCache = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
fun delete(): Boolean {
|
||||
existsCache = false
|
||||
return Files.deleteIfExists(path)
|
||||
}
|
||||
|
||||
fun close() {
|
||||
try {
|
||||
writeChannel.close()
|
||||
readChannel.close()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val handler = object : CompletionHandler<Int, Continuation<Int>> {
|
||||
override fun completed(result: Int, attachment: Continuation<Int>) = attachment.resume(result)
|
||||
override fun failed(exc: Throwable, attachment: Continuation<Int>) = attachment.resumeWithException(exc)
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val handler = object : CompletionHandler<Int, Continuation<Int>> {
|
||||
override fun completed(result: Int, attachment: Continuation<Int>) = attachment.resume(result)
|
||||
override fun failed(exc: Throwable, attachment: Continuation<Int>) = attachment.resumeWithException(exc)
|
||||
}
|
||||
}
|
||||
}
|
186
src/main/kotlin/cn/tursom/core/AtomicBitSet.kt
Normal file
186
src/main/kotlin/cn/tursom/core/AtomicBitSet.kt
Normal file
@ -0,0 +1,186 @@
|
||||
package cn.tursom.core
|
||||
|
||||
import java.io.Serializable
|
||||
import java.lang.reflect.Field
|
||||
import java.util.concurrent.atomic.AtomicLongArray
|
||||
|
||||
class AtomicBitSet(beginSize: Long = 256, val defaultState: Boolean = false) : Serializable {
|
||||
@Volatile
|
||||
private var bitSet = AtomicLongArray(needSize(beginSize))
|
||||
val size
|
||||
get() = bitSet.length().toLong() shl 6
|
||||
val usedSize
|
||||
get() = bitSet.length() * 8
|
||||
val usedSizeStr: String
|
||||
get() {
|
||||
val memory = usedSize
|
||||
val b = memory % 1024
|
||||
val kb = (memory / 1024) % 1024
|
||||
val mb = (memory / 1024 / 1024) % 1024
|
||||
return "${if (mb != 0) "$mb MB " else ""}${if (kb != 0) "$kb KB " else ""} $b Byte"
|
||||
}
|
||||
val trueCount: Long
|
||||
get() {
|
||||
var count = 0L
|
||||
bitSet.array.forEach { count += it.bitCount }
|
||||
return count
|
||||
}
|
||||
|
||||
init {
|
||||
val default = if (defaultState) -1L else 0L
|
||||
for (i in 0 until bitSet.length()) {
|
||||
bitSet[i] = default
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(index: Long): Boolean {
|
||||
return bitSet[(index shr 6).toInt()] and getArr[index.toInt() and 63] != 0L
|
||||
}
|
||||
|
||||
fun up(index: Long): Boolean {
|
||||
val arrayIndex = (index shr 6).toInt()
|
||||
//bitSet[arrayIndex] = bitSet[arrayIndex] or getArr[index.toInt() and 63]
|
||||
val expect = bitSet[arrayIndex]
|
||||
return bitSet.compareAndSet(arrayIndex, expect, expect or getArr[index.toInt() and 63])
|
||||
}
|
||||
|
||||
fun down(index: Long): Boolean {
|
||||
val arrayIndex = (index shr 6).toInt()
|
||||
//bitSet[arrayIndex] = bitSet[arrayIndex] and setArr[index.toInt() and 63]
|
||||
val expect = bitSet[arrayIndex]
|
||||
return bitSet.compareAndSet(arrayIndex, expect, expect and setArr[index.toInt() and 63])
|
||||
}
|
||||
|
||||
fun upAll() {
|
||||
for (i in 0 until bitSet.length()) {
|
||||
bitSet[i] = -1
|
||||
}
|
||||
}
|
||||
|
||||
fun downAll() {
|
||||
for (i in 0 until bitSet.length()) {
|
||||
bitSet[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
fun resize(newSIze: Long): Boolean = synchronized(this) {
|
||||
if (newSIze < this.size) return false
|
||||
|
||||
val newSet = AtomicLongArray(needSize(newSIze))
|
||||
|
||||
bitSet.array.copyInto(newSet.array)
|
||||
|
||||
val default = if (defaultState) -1L else 0
|
||||
for (i in bitSet.length() until newSet.length()) {
|
||||
newSet[i] = default
|
||||
}
|
||||
|
||||
bitSet = newSet
|
||||
return true
|
||||
}
|
||||
|
||||
fun firstUp(): Long {
|
||||
bitSet.forEachIndexed { index, l ->
|
||||
if (l != 0L) {
|
||||
for (i in 0 until 8) {
|
||||
if (l and scanArray[i] != 0L) {
|
||||
val baseIndex = i * 8
|
||||
for (j in 0 until 8) {
|
||||
if (l and getArr[baseIndex + j] != 0L) return index.toLong() * 64 + baseIndex + j
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
fun firstDown(): Long {
|
||||
bitSet.forEachIndexed { index, l ->
|
||||
if (l != -1L) {
|
||||
for (i in 0 until 8) {
|
||||
if (l.inv() and scanArray[i] != 0L) {
|
||||
val baseIndex = i * 8
|
||||
for (j in 0 until 8) {
|
||||
if (l and getArr[baseIndex + j] == 0L) return index.toLong() * 64 + baseIndex + j
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
override fun toString() = "MemoryBitArray(max size=$size, true count=$trueCount, used memory=$usedSizeStr)"
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun needSize(maxIndex: Long) = ((((maxIndex - 1) shr 6) + 1) and 0xffffffff).toInt()
|
||||
|
||||
private val getArr = longArrayOf(
|
||||
1L shl 0, 1L shl 1, 1L shl 2, 1L shl 3,
|
||||
1L shl 4, 1L shl 5, 1L shl 6, 1L shl 7,
|
||||
1L shl 8, 1L shl 9, 1L shl 10, 1L shl 11,
|
||||
1L shl 12, 1L shl 13, 1L shl 14, 1L shl 15,
|
||||
1L shl 16, 1L shl 17, 1L shl 18, 1L shl 19,
|
||||
1L shl 20, 1L shl 21, 1L shl 22, 1L shl 23,
|
||||
1L shl 24, 1L shl 25, 1L shl 26, 1L shl 27,
|
||||
1L shl 28, 1L shl 29, 1L shl 30, 1L shl 31,
|
||||
1L shl 32, 1L shl 33, 1L shl 34, 1L shl 35,
|
||||
1L shl 36, 1L shl 37, 1L shl 38, 1L shl 39,
|
||||
1L shl 40, 1L shl 41, 1L shl 42, 1L shl 43,
|
||||
1L shl 44, 1L shl 45, 1L shl 46, 1L shl 47,
|
||||
1L shl 48, 1L shl 49, 1L shl 50, 1L shl 51,
|
||||
1L shl 52, 1L shl 53, 1L shl 54, 1L shl 55,
|
||||
1L shl 56, 1L shl 57, 1L shl 58, 1L shl 59,
|
||||
1L shl 60, 1L shl 61, 1L shl 62, 1L shl 63
|
||||
)
|
||||
|
||||
private val setArr = LongArray(64) { getArr[it].inv() }
|
||||
|
||||
private val bitCountArray = longArrayOf(
|
||||
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
|
||||
)
|
||||
|
||||
private val scanArray = longArrayOf(
|
||||
0xffL, 0xffL shl 8, 0xffL shl 16, 0xffL shl 24,
|
||||
0xffL shl 32, 0xffL shl 40, 0xffL shl 48, 0xffL shl 56
|
||||
)
|
||||
|
||||
private val Long.bitCount
|
||||
get() = bitCountArray[toInt().and(0xff)] + bitCountArray[shr(8).toInt().and(0xff)] +
|
||||
bitCountArray[shr(16).toInt().and(0xff)] + bitCountArray[shr(24).toInt().and(0xff)] +
|
||||
bitCountArray[shr(32).toInt().and(0xff)] + bitCountArray[shr(40).toInt().and(0xff)] +
|
||||
bitCountArray[shr(48).toInt().and(0xff)] + bitCountArray[shr(56).toInt().and(0xff)]
|
||||
|
||||
private val AtomicLongArray.size get() = length()
|
||||
|
||||
private val array: Field = AtomicLongArray::class.java.getDeclaredField("array")
|
||||
private val AtomicLongArray.array get() = Companion.array.get(this) as LongArray
|
||||
|
||||
init {
|
||||
array.isAccessible = true
|
||||
}
|
||||
|
||||
inline fun AtomicLongArray.forEachIndexed(action: (index: Int, Long) -> Unit) {
|
||||
repeat(length()) {
|
||||
action(it, get(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
src/main/kotlin/cn/tursom/core/CurrentTimeMillisClock.kt
Normal file
22
src/main/kotlin/cn/tursom/core/CurrentTimeMillisClock.kt
Normal file
@ -0,0 +1,22 @@
|
||||
package cn.tursom.core
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor
|
||||
|
||||
|
||||
object CurrentTimeMillisClock {
|
||||
@Volatile
|
||||
private var tick: Long = System.currentTimeMillis()
|
||||
|
||||
val now get() = tick
|
||||
|
||||
init {
|
||||
ScheduledThreadPoolExecutor(1) { runnable ->
|
||||
val thread = Thread(runnable, "current-time-millis")
|
||||
thread.isDaemon = true
|
||||
thread
|
||||
}.scheduleAtFixedRate({ tick = System.currentTimeMillis() }, 1, 1, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
//val now get() = System.currentTimeMillis()
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
package cn.tursom.core
|
||||
|
||||
import cn.tursom.core.bytebuffer.HeapByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
import java.io.*
|
||||
import java.nio.ByteOrder
|
||||
|
||||
@ -544,7 +544,7 @@ private val ByteArrayOutputStream_count = ByteArrayOutputStream::class.java.getD
|
||||
val ByteArrayOutputStream.buf get() = ByteArrayOutputStream_buf.get(this) as ByteArray
|
||||
val ByteArrayOutputStream.count get() = ByteArrayOutputStream_count.get(this) as Int
|
||||
|
||||
fun ByteArray.toByteBuffer() = HeapByteBuffer.wrap(this, 0, size)
|
||||
fun ByteArray.toByteBuffer() = HeapByteBuffer(this, 0, size)
|
||||
|
||||
inline fun <T> Array<T>.forEachIndex(fromIndex: Int, toIndex: Int, action: (T) -> Unit) {
|
||||
for (i in fromIndex..toIndex) {
|
||||
|
26
src/main/kotlin/cn/tursom/core/HeapByteBufferUtil.kt
Normal file
26
src/main/kotlin/cn/tursom/core/HeapByteBufferUtil.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package cn.tursom.core
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
/**
|
||||
* HOOK java.nio.HeapByteBuffer
|
||||
*/
|
||||
object HeapByteBufferUtil {
|
||||
private val field = ByteBuffer::class.java.getDeclaredField("offset")
|
||||
|
||||
init {
|
||||
field.isAccessible = true
|
||||
}
|
||||
|
||||
fun wrap(array: ByteArray, offset: Int = 0, size: Int = array.size - offset): ByteBuffer {
|
||||
val buffer = ByteBuffer.wrap(array, 0, offset + size)
|
||||
//return if (offset == 0) buffer else {
|
||||
// buffer.position(offset)
|
||||
// buffer.slice()
|
||||
//}
|
||||
if (offset > 0) field.set(buffer, offset)
|
||||
return buffer
|
||||
}
|
||||
|
||||
fun wrap(string: String) = wrap(string.toByteArray())
|
||||
}
|
95
src/main/kotlin/cn/tursom/core/LongBitSet.kt
Normal file
95
src/main/kotlin/cn/tursom/core/LongBitSet.kt
Normal file
@ -0,0 +1,95 @@
|
||||
package cn.tursom.core
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
class LongBitSet {
|
||||
private val bitSet = AtomicLong(0)
|
||||
|
||||
val trueCount: Long get() = bitSet.get().bitCount
|
||||
|
||||
fun up(index: Int) = bitSet.compareAndSet(bitSet.get(), bitSet.get() or getArr[index and 63])
|
||||
fun down(index: Int) = bitSet.compareAndSet(bitSet.get(), bitSet.get() and setArr[index and 63])
|
||||
|
||||
fun firstUp(): Int {
|
||||
val l = bitSet.get()
|
||||
if (l != 0L) {
|
||||
for (i in 0 until 8) {
|
||||
if (l and scanArray[i] != 0L) {
|
||||
val baseIndex = i * 8
|
||||
for (j in 0 until 8) {
|
||||
if (l and getArr[baseIndex + j] != 0L) return baseIndex + j
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
fun firstDown(): Int {
|
||||
val l = bitSet.get()
|
||||
if (l != -1L) {
|
||||
for (i in 0 until 8) {
|
||||
if (l.inv() and scanArray[i] != 0L) {
|
||||
val baseIndex = i * 8
|
||||
for (j in 0 until 8) {
|
||||
if (l and getArr[baseIndex + j] == 0L) return baseIndex + j
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val getArr = longArrayOf(
|
||||
1L shl 0, 1L shl 1, 1L shl 2, 1L shl 3,
|
||||
1L shl 4, 1L shl 5, 1L shl 6, 1L shl 7,
|
||||
1L shl 8, 1L shl 9, 1L shl 10, 1L shl 11,
|
||||
1L shl 12, 1L shl 13, 1L shl 14, 1L shl 15,
|
||||
1L shl 16, 1L shl 17, 1L shl 18, 1L shl 19,
|
||||
1L shl 20, 1L shl 21, 1L shl 22, 1L shl 23,
|
||||
1L shl 24, 1L shl 25, 1L shl 26, 1L shl 27,
|
||||
1L shl 28, 1L shl 29, 1L shl 30, 1L shl 31,
|
||||
1L shl 32, 1L shl 33, 1L shl 34, 1L shl 35,
|
||||
1L shl 36, 1L shl 37, 1L shl 38, 1L shl 39,
|
||||
1L shl 40, 1L shl 41, 1L shl 42, 1L shl 43,
|
||||
1L shl 44, 1L shl 45, 1L shl 46, 1L shl 47,
|
||||
1L shl 48, 1L shl 49, 1L shl 50, 1L shl 51,
|
||||
1L shl 52, 1L shl 53, 1L shl 54, 1L shl 55,
|
||||
1L shl 56, 1L shl 57, 1L shl 58, 1L shl 59,
|
||||
1L shl 60, 1L shl 61, 1L shl 62, 1L shl 63
|
||||
)
|
||||
|
||||
private val setArr = LongArray(64) { getArr[it].inv() }
|
||||
|
||||
private val bitCountArray = longArrayOf(
|
||||
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
|
||||
)
|
||||
|
||||
private val scanArray = longArrayOf(
|
||||
0xffL, 0xffL shl 8, 0xffL shl 16, 0xffL shl 24,
|
||||
0xffL shl 32, 0xffL shl 40, 0xffL shl 48, 0xffL shl 56
|
||||
)
|
||||
|
||||
private val Long.bitCount
|
||||
get() = bitCountArray[toInt().and(0xff)] + bitCountArray[shr(8).toInt().and(0xff)] +
|
||||
bitCountArray[shr(16).toInt().and(0xff)] + bitCountArray[shr(24).toInt().and(0xff)] +
|
||||
bitCountArray[shr(32).toInt().and(0xff)] + bitCountArray[shr(40).toInt().and(0xff)] +
|
||||
bitCountArray[shr(48).toInt().and(0xff)] + bitCountArray[shr(56).toInt().and(0xff)]
|
||||
}
|
||||
}
|
31
src/main/kotlin/cn/tursom/core/NonLockLinkedList.kt
Normal file
31
src/main/kotlin/cn/tursom/core/NonLockLinkedList.kt
Normal file
@ -0,0 +1,31 @@
|
||||
package cn.tursom.core
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
open class NonLockLinkedList<T> {
|
||||
private val root = AtomicReference<TaskListNode<T>?>()
|
||||
|
||||
infix fun add(element: T) {
|
||||
val taskNode = TaskListNode(element, root.get())
|
||||
while (!root.compareAndSet(taskNode.next, taskNode)) {
|
||||
taskNode.next = root.get()
|
||||
}
|
||||
}
|
||||
|
||||
infix operator fun invoke(data: T) = add(data)
|
||||
|
||||
fun take(): T? {
|
||||
var node = root.get()
|
||||
while (!root.compareAndSet(node, node?.next)) {
|
||||
node = root.get()
|
||||
}
|
||||
return node?.data
|
||||
}
|
||||
|
||||
private class TaskListNode<T>(
|
||||
val data: T,
|
||||
@Volatile var next: TaskListNode<T>?
|
||||
)
|
||||
|
||||
class NotSupportedException : Exception()
|
||||
}
|
228
src/main/kotlin/cn/tursom/core/buffer/ByteBuffer.kt
Normal file
228
src/main/kotlin/cn/tursom/core/buffer/ByteBuffer.kt
Normal file
@ -0,0 +1,228 @@
|
||||
package cn.tursom.core.buffer
|
||||
|
||||
import cn.tursom.core.forEachIndex
|
||||
import java.io.Closeable
|
||||
import java.io.OutputStream
|
||||
import java.nio.Buffer
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* 针对 java nio 的弱智 ByteBuffer 的简单封装
|
||||
* 支持读写 buffer 分离
|
||||
*/
|
||||
@Suppress("unused")
|
||||
interface ByteBuffer : Closeable {
|
||||
/**
|
||||
* 使用读 buffer,ByteBuffer 实现类有义务维护指针正常推进
|
||||
*/
|
||||
fun <T> readBuffer(block: (java.nio.ByteBuffer) -> T): T {
|
||||
val buffer = readBuffer()
|
||||
return try {
|
||||
block(buffer)
|
||||
} finally {
|
||||
finishRead(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用写 buffer,ByteBuffer 实现类有义务维护指针正常推进
|
||||
*/
|
||||
fun <T> writeBuffer(block: (java.nio.ByteBuffer) -> T): T {
|
||||
val buffer = writeBuffer()
|
||||
return try {
|
||||
block(buffer)
|
||||
} finally {
|
||||
finishWrite(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
val readable: Int get() = writePosition - readPosition
|
||||
val writeable: Int get() = capacity - readPosition
|
||||
|
||||
val hasArray: Boolean
|
||||
val array: ByteArray
|
||||
|
||||
val capacity: Int
|
||||
val arrayOffset: Int
|
||||
var writePosition: Int
|
||||
var readPosition: Int
|
||||
|
||||
val closed: Boolean get() = false
|
||||
val resized: Boolean
|
||||
|
||||
override fun close() {
|
||||
}
|
||||
|
||||
fun readBuffer(): java.nio.ByteBuffer
|
||||
fun finishRead(buffer: java.nio.ByteBuffer) {
|
||||
readPosition = buffer.position()
|
||||
}
|
||||
|
||||
fun writeBuffer(): java.nio.ByteBuffer
|
||||
fun finishWrite(buffer: java.nio.ByteBuffer) {
|
||||
readPosition = buffer.position()
|
||||
}
|
||||
|
||||
fun reset()
|
||||
fun slice(offset: Int, size: Int): ByteBuffer
|
||||
|
||||
/**
|
||||
* @return 底层 nio buffer 是否已更新
|
||||
*/
|
||||
fun resize(newSize: Int): Boolean
|
||||
|
||||
val writeOffset: Int get() = arrayOffset + writePosition
|
||||
val readOffset: Int get() = arrayOffset + readPosition
|
||||
|
||||
fun clear() {
|
||||
readPosition = 0
|
||||
writePosition = 0
|
||||
}
|
||||
|
||||
fun get(): Byte = read { it.get() }
|
||||
fun getChar(): Char = read { it.char }
|
||||
fun getShort(): Short = read { it.short }
|
||||
fun getInt(): Int = read { it.int }
|
||||
fun getLong(): Long = read { it.long }
|
||||
fun getFloat(): Float = read { it.float }
|
||||
fun getDouble(): Double = read { it.double }
|
||||
|
||||
fun getBytes(size: Int = readable): ByteArray = read {
|
||||
val bytes = ByteArray(size)
|
||||
it.get(bytes)
|
||||
bytes
|
||||
}
|
||||
|
||||
fun getString(size: Int = readable): String = String(getBytes(size))
|
||||
|
||||
fun toString(size: Int): String {
|
||||
val bytes = getBytes(size)
|
||||
readPosition -= bytes.size
|
||||
return String(bytes)
|
||||
}
|
||||
|
||||
fun writeTo(buffer: ByteArray, bufferOffset: Int = 0, size: Int = min(readable, buffer.size)): Int {
|
||||
val readSize = min(readable, size)
|
||||
if (hasArray) {
|
||||
array.copyInto(buffer, bufferOffset, readOffset, readOffset + readSize)
|
||||
readPosition += readOffset
|
||||
reset()
|
||||
} else {
|
||||
read {
|
||||
it.put(buffer, bufferOffset, readSize)
|
||||
}
|
||||
}
|
||||
return readSize
|
||||
}
|
||||
|
||||
fun writeTo(os: OutputStream): Int {
|
||||
val size = readable
|
||||
if (hasArray) {
|
||||
os.write(array, readOffset, size)
|
||||
readPosition += size
|
||||
reset()
|
||||
} else {
|
||||
val buffer = ByteArray(1024)
|
||||
read {
|
||||
while (it.remaining() > 0) {
|
||||
it.put(buffer)
|
||||
os.write(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
fun writeTo(buffer: ByteBuffer): Int {
|
||||
val size = min(readable, buffer.readable)
|
||||
if (hasArray) {
|
||||
buffer.put(array, readOffset, size)
|
||||
readPosition += size
|
||||
reset()
|
||||
} else {
|
||||
read { read ->
|
||||
buffer.write { write -> write.put(read) }
|
||||
}
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
fun toByteArray() = getBytes()
|
||||
|
||||
|
||||
/*
|
||||
* 数据写入方法
|
||||
*/
|
||||
|
||||
fun put(byte: Byte): Unit = write { it.put(byte) }
|
||||
fun put(char: Char): Unit = write { it.putChar(char) }
|
||||
fun put(short: Short): Unit = write { it.putShort(short) }
|
||||
fun put(int: Int): Unit = write { it.putInt(int) }
|
||||
fun put(long: Long): Unit = write { it.putLong(long) }
|
||||
fun put(float: Float): Unit = write { it.putFloat(float) }
|
||||
fun put(double: Double): Unit = write { it.putDouble(double) }
|
||||
fun put(str: String): Unit = put(str.toByteArray())
|
||||
fun put(buffer: ByteBuffer): Int = buffer.writeTo(this)
|
||||
fun put(byteArray: ByteArray, startIndex: Int = 0, endIndex: Int = byteArray.size - startIndex) {
|
||||
if (hasArray) {
|
||||
byteArray.copyInto(array, writeOffset, startIndex, endIndex)
|
||||
writePosition += endIndex - startIndex
|
||||
} else {
|
||||
write {
|
||||
it.put(byteArray, startIndex, endIndex - startIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun put(array: CharArray, index: Int = 0, size: Int = array.size - index) {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
|
||||
fun put(array: ShortArray, index: Int = 0, size: Int = array.size - index) {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
|
||||
fun put(array: IntArray, index: Int = 0, size: Int = array.size - index) {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
|
||||
fun put(array: LongArray, index: Int = 0, size: Int = array.size - index) {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
|
||||
fun put(array: FloatArray, index: Int = 0, size: Int = array.size - index) {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
|
||||
fun put(array: DoubleArray, index: Int = 0, size: Int = array.size - index) {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
|
||||
fun fill(byte: Byte) {
|
||||
readPosition = 0
|
||||
writePosition = 0
|
||||
write {
|
||||
while (it.remaining() != 0) {
|
||||
it.put(byte)
|
||||
}
|
||||
}
|
||||
writePosition = 0
|
||||
}
|
||||
|
||||
fun split(maxSize: Int): Array<out ByteBuffer> {
|
||||
val size = (((capacity - 1) / maxSize) + 1).and(0x7fff_ffff)
|
||||
return Array(size) {
|
||||
if (it != size - 1) {
|
||||
slice(it * maxSize, maxSize)
|
||||
} else {
|
||||
slice(it * maxSize, capacity - it * maxSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun readAllSize(): Int {
|
||||
val size = readable
|
||||
readPosition += size
|
||||
return size
|
||||
}
|
||||
}
|
79
src/main/kotlin/cn/tursom/core/buffer/ByteBufferExtension.kt
Normal file
79
src/main/kotlin/cn/tursom/core/buffer/ByteBufferExtension.kt
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 这个文件包含了一些可能出错的操作的封装
|
||||
* 还有一些是以内联形式封装以提高运行效率
|
||||
*/
|
||||
|
||||
package cn.tursom.core.buffer
|
||||
|
||||
import cn.tursom.buffer.MultipleByteBuffer
|
||||
import cn.tursom.core.buffer.impl.ArrayByteBuffer
|
||||
import java.nio.channels.SocketChannel
|
||||
|
||||
|
||||
/**
|
||||
* 使用读 buffer,ByteBuffer 实现类有义务维护指针正常推进
|
||||
*/
|
||||
inline fun <T> ByteBuffer.read(block: (java.nio.ByteBuffer) -> T): T {
|
||||
val buffer = readBuffer()
|
||||
return try {
|
||||
block(buffer)
|
||||
} finally {
|
||||
finishRead(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用写 buffer,ByteBuffer 实现类有义务维护指针正常推进
|
||||
*/
|
||||
inline fun <T> ByteBuffer.write(block: (java.nio.ByteBuffer) -> T): T {
|
||||
val buffer = writeBuffer()
|
||||
return try {
|
||||
block(buffer)
|
||||
} finally {
|
||||
finishWrite(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
fun SocketChannel.read(buffer: ByteBuffer): Int {
|
||||
return if (buffer is MultipleByteBuffer) {
|
||||
buffer.writeBuffers { read(it) }.toInt()
|
||||
} else {
|
||||
buffer.write { read(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun SocketChannel.write(buffer: ByteBuffer): Int {
|
||||
return if (buffer is MultipleByteBuffer) {
|
||||
buffer.readBuffers { write(it) }.toInt()
|
||||
} else {
|
||||
buffer.read { write(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun SocketChannel.read(buffer: MultipleByteBuffer): Long {
|
||||
return buffer.writeBuffers { read(it) }
|
||||
}
|
||||
|
||||
fun SocketChannel.write(buffer: MultipleByteBuffer): Long {
|
||||
return buffer.readBuffers { write(it) }
|
||||
}
|
||||
|
||||
fun SocketChannel.read(buffers: Array<out ByteBuffer>): Long {
|
||||
val bufferArray = Array(buffers.size) { buffers[it].writeBuffer() }
|
||||
return try {
|
||||
read(bufferArray)
|
||||
} finally {
|
||||
buffers.forEachIndexed { index, byteBuffer -> byteBuffer.finishWrite(bufferArray[index]) }
|
||||
}
|
||||
}
|
||||
|
||||
fun SocketChannel.write(buffers: Array<out ByteBuffer>): Long {
|
||||
val bufferArray = Array(buffers.size) { buffers[it].readBuffer() }
|
||||
return try {
|
||||
write(bufferArray)
|
||||
} finally {
|
||||
buffers.forEachIndexed { index, byteBuffer -> byteBuffer.finishRead(bufferArray[index]) }
|
||||
}
|
||||
}
|
||||
|
||||
fun Array<out ByteBuffer>.asMultipleByteBuffer() = ArrayByteBuffer(*this)
|
@ -0,0 +1,8 @@
|
||||
package cn.tursom.buffer
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
|
||||
interface MarkableByteBuffer : ByteBuffer {
|
||||
fun mark()
|
||||
fun resume()
|
||||
}
|
51
src/main/kotlin/cn/tursom/core/buffer/MultipleByteBuffer.kt
Normal file
51
src/main/kotlin/cn/tursom/core/buffer/MultipleByteBuffer.kt
Normal file
@ -0,0 +1,51 @@
|
||||
package cn.tursom.buffer
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.ListByteBuffer
|
||||
import java.io.Closeable
|
||||
|
||||
@Suppress("unused")
|
||||
interface MultipleByteBuffer : List<ByteBuffer>, Closeable {
|
||||
val buffers: Array<out ByteBuffer> get() = toTypedArray()
|
||||
|
||||
/**
|
||||
* 使用读 buffer,ByteBuffer 实现类有义务维护指针正常推进
|
||||
*/
|
||||
fun <T> readBuffers(block: (Array<out java.nio.ByteBuffer>) -> T): T {
|
||||
val buffer = readBuffers()
|
||||
return try {
|
||||
block(buffer)
|
||||
} finally {
|
||||
finishRead(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用写 buffer,ByteBuffer 实现类有义务维护指针正常推进
|
||||
*/
|
||||
fun <T> writeBuffers(block: (Array<out java.nio.ByteBuffer>) -> T): T {
|
||||
val buffer = writeBuffers()
|
||||
return try {
|
||||
block(buffer)
|
||||
} finally {
|
||||
finishWrite(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
fun readBuffers(): Array<out java.nio.ByteBuffer> = Array(size) { this[it].readBuffer() }
|
||||
fun writeBuffers(): Array<out java.nio.ByteBuffer> = Array(size) { this[it].writeBuffer() }
|
||||
|
||||
fun finishRead(buffers: Array<out java.nio.ByteBuffer>) = buffers.forEachIndexed { index, byteBuffer ->
|
||||
this[index].finishRead(byteBuffer)
|
||||
}
|
||||
|
||||
fun finishWrite(buffers: Array<out java.nio.ByteBuffer>) = buffers.forEachIndexed { index, byteBuffer ->
|
||||
this[index].finishWrite(byteBuffer)
|
||||
}
|
||||
|
||||
override fun close() = forEach(ByteBuffer::close)
|
||||
fun slice(offset: Int, size: Int): MultipleByteBuffer = ListByteBuffer(subList(offset, offset + size))
|
||||
fun fill(byte: Byte) = forEach { it.fill(byte) }
|
||||
fun clear() = forEach(ByteBuffer::clear)
|
||||
fun reset() = forEach(ByteBuffer::reset)
|
||||
}
|
7
src/main/kotlin/cn/tursom/core/buffer/ProxyByteBuffer.kt
Normal file
7
src/main/kotlin/cn/tursom/core/buffer/ProxyByteBuffer.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package cn.tursom.buffer
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
|
||||
interface ProxyByteBuffer : ByteBuffer {
|
||||
val agent: ByteBuffer
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package cn.tursom.core.buffer.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.buffer.MultipleByteBuffer
|
||||
|
||||
class ArrayByteBuffer(
|
||||
override vararg val buffers: ByteBuffer
|
||||
) : MultipleByteBuffer, List<ByteBuffer> by buffers.asList()
|
@ -0,0 +1,8 @@
|
||||
package cn.tursom.core.buffer.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.buffer.MultipleByteBuffer
|
||||
|
||||
class ArrayListByteBuffer : MultipleByteBuffer, MutableList<ByteBuffer> by ArrayList() {
|
||||
override fun clear() = super.clear()
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package cn.tursom.core.buffer.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
|
||||
class DirectByteBuffer(private var buffer: java.nio.ByteBuffer) : ByteBuffer {
|
||||
constructor(size: Int) : this(java.nio.ByteBuffer.allocateDirect(size))
|
||||
|
||||
override val hasArray: Boolean = false
|
||||
override val array: ByteArray get() = buffer.array()
|
||||
override val capacity: Int get() = buffer.capacity()
|
||||
override val arrayOffset: Int = 0
|
||||
override var writePosition: Int = 0
|
||||
override var readPosition: Int = 0
|
||||
override var resized: Boolean = false
|
||||
|
||||
override fun readBuffer(): java.nio.ByteBuffer {
|
||||
if (buffer.limit() != writePosition)
|
||||
buffer.limit(writePosition)
|
||||
if (buffer.position() != readPosition)
|
||||
buffer.position(readPosition)
|
||||
return buffer
|
||||
}
|
||||
|
||||
override fun writeBuffer(): java.nio.ByteBuffer {
|
||||
if (buffer.limit() != capacity)
|
||||
buffer.limit(capacity)
|
||||
if (buffer.position() != writePosition)
|
||||
buffer.position(writePosition)
|
||||
return buffer
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
buffer.limit(writePosition)
|
||||
buffer.position(readPosition)
|
||||
buffer.compact()
|
||||
readPosition = buffer.position()
|
||||
writePosition = buffer.limit()
|
||||
}
|
||||
|
||||
override fun slice(offset: Int, size: Int): ByteBuffer {
|
||||
buffer.limit(offset + size)
|
||||
buffer.position(offset)
|
||||
return DirectByteBuffer(buffer.slice())
|
||||
}
|
||||
|
||||
override fun resize(newSize: Int): Boolean {
|
||||
if (newSize <= buffer.capacity()) return false
|
||||
resized = true
|
||||
val newBuf = java.nio.ByteBuffer.allocateDirect(newSize)
|
||||
newBuf.put(readBuffer())
|
||||
buffer = newBuf
|
||||
writePosition = readable
|
||||
readPosition = 0
|
||||
return true
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "DirectByteBuffer(buffer=$buffer, hasArray=$hasArray, capacity=$capacity, writePosition=$writePosition, readPosition=$readPosition)"
|
||||
}
|
||||
|
||||
override fun fill(byte: Byte) {
|
||||
readPosition = 0
|
||||
writePosition = 0
|
||||
buffer.clear()
|
||||
while (buffer.remaining() != 0) {
|
||||
buffer.put(byte)
|
||||
}
|
||||
}
|
||||
}
|
76
src/main/kotlin/cn/tursom/core/buffer/impl/HeapByteBuffer.kt
Normal file
76
src/main/kotlin/cn/tursom/core/buffer/impl/HeapByteBuffer.kt
Normal file
@ -0,0 +1,76 @@
|
||||
package cn.tursom.core.buffer.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.HeapByteBufferUtil
|
||||
|
||||
class HeapByteBuffer(private var buffer: java.nio.ByteBuffer) : ByteBuffer {
|
||||
constructor(size: Int) : this(java.nio.ByteBuffer.allocate(size))
|
||||
constructor(bytes: ByteArray, offset: Int = 0, size: Int = bytes.size - offset)
|
||||
: this(HeapByteBufferUtil.wrap(bytes, offset, size)) {
|
||||
readPosition = offset
|
||||
writePosition = offset + size
|
||||
}
|
||||
|
||||
init {
|
||||
assert(buffer.hasArray())
|
||||
}
|
||||
|
||||
override val hasArray: Boolean = true
|
||||
override val array: ByteArray get() = buffer.array()
|
||||
override val capacity: Int get() = buffer.capacity()
|
||||
override val arrayOffset: Int get() = buffer.arrayOffset()
|
||||
override var writePosition: Int = 0
|
||||
override var readPosition: Int = 0
|
||||
override var resized: Boolean = false
|
||||
|
||||
override fun readBuffer(): java.nio.ByteBuffer {
|
||||
if (buffer.limit() != writePosition)
|
||||
buffer.limit(writePosition)
|
||||
if (buffer.position() != readPosition)
|
||||
buffer.position(readPosition)
|
||||
return buffer
|
||||
}
|
||||
|
||||
override fun writeBuffer(): java.nio.ByteBuffer {
|
||||
if (buffer.limit() != capacity)
|
||||
buffer.limit(capacity)
|
||||
if (buffer.position() != writePosition)
|
||||
buffer.position(writePosition)
|
||||
return buffer
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
buffer.limit(writePosition)
|
||||
buffer.position(readPosition)
|
||||
buffer.compact()
|
||||
readPosition = buffer.position()
|
||||
writePosition = buffer.limit()
|
||||
}
|
||||
|
||||
override fun slice(offset: Int, size: Int): ByteBuffer {
|
||||
buffer.limit(offset + size)
|
||||
buffer.position(offset)
|
||||
return HeapByteBuffer(buffer.slice())
|
||||
}
|
||||
|
||||
override fun resize(newSize: Int): Boolean {
|
||||
if (newSize <= buffer.capacity()) return false
|
||||
resized = true
|
||||
val newBuf = java.nio.ByteBuffer.allocate(newSize)
|
||||
newBuf.put(array, readOffset, readable)
|
||||
buffer = newBuf
|
||||
writePosition = readable
|
||||
readPosition = 0
|
||||
return true
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "HeapByteBuffer(buffer=$buffer, hasArray=$hasArray, capacity=$capacity, arrayOffset=$arrayOffset, writePosition=$writePosition, readPosition=$readPosition)"
|
||||
}
|
||||
|
||||
override fun fill(byte: Byte) {
|
||||
writePosition = 0
|
||||
readPosition = 0
|
||||
array.fill(byte, arrayOffset, arrayOffset + capacity)
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.tursom.core.buffer.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.buffer.ProxyByteBuffer
|
||||
import cn.tursom.core.pool.MemoryPool
|
||||
|
||||
class InstantByteBuffer(
|
||||
override val agent: ByteBuffer,
|
||||
val pool: MemoryPool
|
||||
) : ProxyByteBuffer, ByteBuffer by agent {
|
||||
override var closed = false
|
||||
|
||||
override fun close() {
|
||||
agent.close()
|
||||
pool.free(this)
|
||||
closed = true
|
||||
}
|
||||
|
||||
override fun toString() = "InstantByteBuffer(agent=$agent)"
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package cn.tursom.core.buffer.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.buffer.MultipleByteBuffer
|
||||
|
||||
class ListByteBuffer(bufferList: List<ByteBuffer>) : MultipleByteBuffer, List<ByteBuffer> by bufferList
|
@ -0,0 +1,20 @@
|
||||
package cn.tursom.core.buffer.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.buffer.MarkableByteBuffer
|
||||
import cn.tursom.buffer.ProxyByteBuffer
|
||||
|
||||
class MarkedByteBuffer(override val agent: ByteBuffer) : ProxyByteBuffer, MarkableByteBuffer, ByteBuffer by agent {
|
||||
private var writeMark = 0
|
||||
private var readMark = 0
|
||||
|
||||
override fun mark() {
|
||||
writeMark = writePosition
|
||||
readMark = readPosition
|
||||
}
|
||||
|
||||
override fun resume() {
|
||||
writePosition = writeMark
|
||||
readPosition = readMark
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package cn.tursom.core.buffer.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.buffer.ProxyByteBuffer
|
||||
import cn.tursom.core.pool.MemoryPool
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
/**
|
||||
* 在被垃圾回收时能保证释放占用的内存池内存
|
||||
*/
|
||||
class PooledByteBuffer(
|
||||
override val agent: ByteBuffer,
|
||||
val pool: MemoryPool,
|
||||
val token: Int
|
||||
) : ProxyByteBuffer, ByteBuffer by agent {
|
||||
/**
|
||||
* 这个变量保证 buffer 不会被释放多次
|
||||
*/
|
||||
private val open = AtomicBoolean(true)
|
||||
private val childCount = AtomicInteger(0)
|
||||
override val resized get() = agent.resized
|
||||
|
||||
override val closed: Boolean get() = !open.get() && !resized
|
||||
override fun close() {
|
||||
if (childCount.get() == 0) {
|
||||
if (open.compareAndSet(true, false)) {
|
||||
pool.free(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun resize(newSize: Int): Boolean {
|
||||
val successful = agent.resize(newSize)
|
||||
if (successful) {
|
||||
close()
|
||||
}
|
||||
return successful
|
||||
}
|
||||
|
||||
override fun slice(offset: Int, size: Int): ByteBuffer {
|
||||
return SplitByteBuffer(this, childCount, agent.slice(offset, size))
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PooledByteBuffer(buffer=$agent, pool=$pool, token=$token, open=$open)"
|
||||
}
|
||||
|
||||
protected fun finalize() {
|
||||
pool.free(this)
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package cn.tursom.core.buffer.impl
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.buffer.ProxyByteBuffer
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class SplitByteBuffer(
|
||||
private val parent: ByteBuffer,
|
||||
private val childCount: AtomicInteger,
|
||||
override val agent: ByteBuffer
|
||||
) : ProxyByteBuffer, ByteBuffer by agent {
|
||||
init {
|
||||
childCount.incrementAndGet()
|
||||
}
|
||||
|
||||
private val atomicClosed = AtomicBoolean(false)
|
||||
|
||||
override val closed: Boolean get() = atomicClosed.get()
|
||||
|
||||
override fun close() {
|
||||
if (atomicClosed.compareAndSet(false, true)) {
|
||||
agent.close()
|
||||
childCount.decrementAndGet()
|
||||
if (childCount.get() == 0 && (parent.closed || parent.resized)) {
|
||||
parent.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun slice(offset: Int, size: Int): ByteBuffer {
|
||||
return SplitByteBuffer(parent, childCount, agent.slice(offset, size))
|
||||
}
|
||||
|
||||
override fun resize(newSize: Int): Boolean {
|
||||
val successful = agent.resize(newSize)
|
||||
if (successful) {
|
||||
close()
|
||||
}
|
||||
return successful
|
||||
}
|
||||
|
||||
protected fun finalize() {
|
||||
close()
|
||||
}
|
||||
}
|
@ -1,445 +0,0 @@
|
||||
package cn.tursom.core.bytebuffer
|
||||
|
||||
import cn.tursom.core.forEachIndex
|
||||
import cn.tursom.core.logE
|
||||
import sun.reflect.generics.reflectiveObjects.NotImplementedException
|
||||
import java.io.OutputStream
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.math.min
|
||||
|
||||
interface AdvanceByteBuffer {
|
||||
val nioBuffer: ByteBuffer
|
||||
val nioBuffers: Array<out ByteBuffer> get() = arrayOf(nioBuffer)
|
||||
|
||||
/**
|
||||
* 各种位置变量
|
||||
*/
|
||||
val readOnly: Boolean
|
||||
val bufferCount: Int get() = 1
|
||||
|
||||
var writePosition: Int
|
||||
var limit: Int
|
||||
val capacity: Int
|
||||
val hasArray: Boolean
|
||||
val array: ByteArray
|
||||
val arrayOffset: Int
|
||||
var readPosition: Int
|
||||
val readOffset: Int get() = arrayOffset + readPosition
|
||||
val readableSize: Int
|
||||
val available: Int get() = readableSize
|
||||
val writeOffset: Int get() = arrayOffset + writePosition
|
||||
val writeableSize: Int get() = limit - writePosition
|
||||
val size: Int
|
||||
val readMode: Boolean
|
||||
|
||||
|
||||
fun readMode()
|
||||
fun resumeWriteMode(usedSize: Int = 0)
|
||||
|
||||
fun needReadSize(size: Int) {
|
||||
if (readableSize < size) throw OutOfBufferException()
|
||||
}
|
||||
|
||||
fun useReadSize(size: Int): Int {
|
||||
needReadSize(size)
|
||||
readPosition += size
|
||||
return size
|
||||
}
|
||||
|
||||
fun take(size: Int): Int {
|
||||
needReadSize(size)
|
||||
val offset = readOffset
|
||||
readPosition += size
|
||||
return offset
|
||||
}
|
||||
|
||||
fun push(size: Int): Int {
|
||||
val offset = writeOffset
|
||||
writePosition += size
|
||||
return offset
|
||||
}
|
||||
|
||||
fun readAllSize() = useReadSize(readableSize)
|
||||
fun takeAll() = take(readableSize)
|
||||
|
||||
fun clear()
|
||||
|
||||
fun reset() {
|
||||
if (hasArray) {
|
||||
array.copyInto(array, arrayOffset, readOffset, arrayOffset + writePosition)
|
||||
writePosition = readableSize
|
||||
readPosition = 0
|
||||
} else {
|
||||
readMode()
|
||||
nioBuffer.compact()
|
||||
val writePosition = readPosition
|
||||
resumeWriteMode()
|
||||
readPosition = 0
|
||||
this.writePosition = writePosition
|
||||
}
|
||||
}
|
||||
|
||||
fun reset(outputStream: OutputStream) {
|
||||
if (hasArray) {
|
||||
outputStream.write(array, readOffset, arrayOffset + writePosition)
|
||||
} else {
|
||||
outputStream.write(getBytes())
|
||||
}
|
||||
writePosition = 0
|
||||
readPosition = 0
|
||||
}
|
||||
|
||||
fun requireAvailableSize(size: Int) {
|
||||
if (limit - readPosition < size) reset()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 数据获取方法
|
||||
*/
|
||||
|
||||
|
||||
fun get(): Byte = if (readMode) {
|
||||
nioBuffer.get()
|
||||
} else {
|
||||
readMode()
|
||||
val value = nioBuffer.get()
|
||||
resumeWriteMode()
|
||||
value
|
||||
}
|
||||
|
||||
fun getChar(): Char = if (readMode) {
|
||||
nioBuffer.char
|
||||
} else {
|
||||
readMode()
|
||||
val value = nioBuffer.char
|
||||
resumeWriteMode()
|
||||
value
|
||||
}
|
||||
|
||||
fun getShort(): Short = if (readMode) {
|
||||
nioBuffer.short
|
||||
} else {
|
||||
readMode()
|
||||
val value = nioBuffer.short
|
||||
resumeWriteMode()
|
||||
value
|
||||
}
|
||||
|
||||
fun getInt(): Int = if (readMode) {
|
||||
nioBuffer.int
|
||||
} else {
|
||||
readMode()
|
||||
val value = nioBuffer.int
|
||||
resumeWriteMode()
|
||||
value
|
||||
}
|
||||
|
||||
fun getLong(): Long = if (readMode) {
|
||||
nioBuffer.long
|
||||
} else {
|
||||
readMode()
|
||||
val value = nioBuffer.long
|
||||
resumeWriteMode()
|
||||
value
|
||||
}
|
||||
|
||||
fun getFloat(): Float = if (readMode) {
|
||||
nioBuffer.float
|
||||
} else {
|
||||
readMode()
|
||||
val value = nioBuffer.float
|
||||
resumeWriteMode()
|
||||
value
|
||||
}
|
||||
|
||||
fun getDouble(): Double = if (readMode) {
|
||||
nioBuffer.double
|
||||
} else {
|
||||
readMode()
|
||||
val value = nioBuffer.double
|
||||
resumeWriteMode()
|
||||
value
|
||||
}
|
||||
|
||||
|
||||
fun getBytes(size: Int = readableSize): ByteArray = if (readMode) {
|
||||
val bytes = ByteArray(size)
|
||||
nioBuffer.get(bytes)
|
||||
readPosition = writePosition
|
||||
bytes
|
||||
} else {
|
||||
readMode()
|
||||
val bytes = ByteArray(size)
|
||||
nioBuffer.get(bytes)
|
||||
readPosition = writePosition
|
||||
resumeWriteMode()
|
||||
bytes
|
||||
}
|
||||
|
||||
|
||||
fun getString(size: Int = readableSize): String = String(getBytes())
|
||||
fun toString(size: Int): String {
|
||||
//logE("AdvanceByteBuffer.toString(size: Int): $this")
|
||||
//val rp = readPosition
|
||||
val bytes = getBytes(size)
|
||||
//readPosition = rp
|
||||
//logE("AdvanceByteBuffer.toString(size: Int): $this")
|
||||
return String(bytes)
|
||||
}
|
||||
|
||||
fun writeTo(buffer: ByteArray, bufferOffset: Int = 0, size: Int = min(readableSize, buffer.size)): Int {
|
||||
val readSize = min(readableSize, size)
|
||||
if (hasArray) {
|
||||
array.copyInto(buffer, bufferOffset, readOffset, readOffset + readSize)
|
||||
readPosition += readOffset
|
||||
reset()
|
||||
} else {
|
||||
readBuffer {
|
||||
it.put(buffer, bufferOffset, readSize)
|
||||
}
|
||||
}
|
||||
return readSize
|
||||
}
|
||||
|
||||
fun writeTo(os: OutputStream): Int {
|
||||
val size = readableSize
|
||||
if (hasArray) {
|
||||
os.write(array, arrayOffset + readPosition, size)
|
||||
readPosition += size
|
||||
reset()
|
||||
} else {
|
||||
val buffer = ByteArray(1024)
|
||||
readBuffer {
|
||||
while (it.remaining() > 0) {
|
||||
it.put(buffer)
|
||||
os.write(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
fun writeTo(buffer: AdvanceByteBuffer): Int {
|
||||
val size = min(readableSize, buffer.writeableSize)
|
||||
if (hasArray && buffer.hasArray) {
|
||||
array.copyInto(buffer.array, buffer.writeOffset, readOffset, readOffset + size)
|
||||
buffer.writePosition += size
|
||||
readPosition += size
|
||||
reset()
|
||||
} else {
|
||||
readBuffer {
|
||||
buffer.nioBuffer.put(it)
|
||||
}
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
fun toByteArray() = getBytes()
|
||||
|
||||
|
||||
/*
|
||||
* 数据写入方法
|
||||
*/
|
||||
|
||||
fun put(byte: Byte) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
nioBuffer.put(byte)
|
||||
readMode()
|
||||
} else {
|
||||
nioBuffer.put(byte)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(char: Char) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
nioBuffer.putChar(char)
|
||||
readMode()
|
||||
} else {
|
||||
nioBuffer.putChar(char)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(short: Short) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
nioBuffer.putShort(short)
|
||||
readMode()
|
||||
} else {
|
||||
nioBuffer.putShort(short)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(int: Int) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
nioBuffer.putInt(int)
|
||||
readMode()
|
||||
} else {
|
||||
nioBuffer.putInt(int)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(long: Long) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
nioBuffer.putLong(long)
|
||||
readMode()
|
||||
} else {
|
||||
nioBuffer.putLong(long)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(float: Float) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
nioBuffer.putFloat(float)
|
||||
readMode()
|
||||
} else {
|
||||
nioBuffer.putFloat(float)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(double: Double) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
nioBuffer.putDouble(double)
|
||||
readMode()
|
||||
} else {
|
||||
nioBuffer.putDouble(double)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(str: String) {
|
||||
put(str.toByteArray())
|
||||
}
|
||||
|
||||
fun put(byteArray: ByteArray, startIndex: Int = 0, endIndex: Int = byteArray.size - startIndex) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
nioBuffer.put(byteArray, startIndex, endIndex - startIndex)
|
||||
readMode()
|
||||
} else {
|
||||
nioBuffer.put(byteArray, startIndex, endIndex - startIndex)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(array: CharArray, index: Int = 0, size: Int = array.size - index) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
readMode()
|
||||
} else {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(array: ShortArray, index: Int = 0, size: Int = array.size - index) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
readMode()
|
||||
} else {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(array: IntArray, index: Int = 0, size: Int = array.size - index) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
readMode()
|
||||
} else {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(array: LongArray, index: Int = 0, size: Int = array.size - index) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
readMode()
|
||||
} else {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(array: FloatArray, index: Int = 0, size: Int = array.size - index) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
readMode()
|
||||
} else {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
}
|
||||
|
||||
fun put(array: DoubleArray, index: Int = 0, size: Int = array.size - index) {
|
||||
if (readMode) {
|
||||
resumeWriteMode()
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
readMode()
|
||||
} else {
|
||||
array.forEachIndex(index, index + size - 1, this::put)
|
||||
}
|
||||
}
|
||||
|
||||
fun peekString(size: Int = readableSize): String {
|
||||
val readP = readPosition
|
||||
val str = getString(size)
|
||||
readPosition = readP
|
||||
return str
|
||||
}
|
||||
|
||||
fun <T> readBuffer(action: (nioBuffer: ByteBuffer) -> T): T = readNioBuffer(action)
|
||||
fun <T> writeBuffer(action: (nioBuffer: ByteBuffer) -> T): T = writeNioBuffer(action)
|
||||
|
||||
suspend fun <T> readSuspendBuffer(action: suspend (nioBuffer: ByteBuffer) -> T): T = readNioBuffer { action(it) }
|
||||
suspend fun <T> writeSuspendBuffer(action: suspend (nioBuffer: ByteBuffer) -> T): T = writeNioBuffer { action(it) }
|
||||
|
||||
fun split(from: Int = readPosition, to: Int = writePosition): AdvanceByteBuffer {
|
||||
return if (hasArray) {
|
||||
ByteArrayAdvanceByteBuffer(array, arrayOffset + readPosition, to - from)
|
||||
} else {
|
||||
throw NotImplementedException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String
|
||||
}
|
||||
|
||||
inline fun <T> AdvanceByteBuffer.readNioBuffer(action: (nioBuffer: ByteBuffer) -> T): T {
|
||||
val readMode = this.readMode
|
||||
readMode()
|
||||
val buffer = nioBuffer
|
||||
val position = readPosition
|
||||
val bufferPosition = nioBuffer.position()
|
||||
return try {
|
||||
//logE(buffer.toString())
|
||||
action(buffer)
|
||||
} finally {
|
||||
if (!readMode) {
|
||||
resumeWriteMode(buffer.position() - position)
|
||||
readPosition = position + (buffer.position() - bufferPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> AdvanceByteBuffer.writeNioBuffer(action: (nioBuffer: ByteBuffer) -> T): T {
|
||||
val readMode = readMode
|
||||
resumeWriteMode()
|
||||
val buffer = nioBuffer
|
||||
val position = writePosition
|
||||
val bufferPosition = nioBuffer.position()
|
||||
return try {
|
||||
action(buffer)
|
||||
} finally {
|
||||
if (readMode) {
|
||||
writePosition = position + (buffer.position() - bufferPosition)
|
||||
readMode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
package cn.tursom.core.bytebuffer
|
||||
|
||||
import cn.tursom.core.*
|
||||
import java.io.OutputStream
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class ByteArrayAdvanceByteBuffer(
|
||||
override val array: ByteArray,
|
||||
val offset: Int = 0,
|
||||
override val size: Int = array.size - offset,
|
||||
override var readPosition: Int = 0,
|
||||
override var writePosition: Int = size
|
||||
) : AdvanceByteBuffer {
|
||||
constructor(size: Int) : this(ByteArray(size), 0, size, 0, 0)
|
||||
|
||||
override val hasArray: Boolean get() = true
|
||||
override var readOnly: Boolean = false
|
||||
override val nioBuffer: ByteBuffer
|
||||
get() = if (readMode) readByteBuffer
|
||||
else writeByteBuffer
|
||||
override var limit: Int = size
|
||||
override val capacity: Int get() = size
|
||||
override val arrayOffset: Int get() = offset
|
||||
override val available: Int
|
||||
get() = readableSize
|
||||
override val writeableSize: Int get() = limit - writePosition
|
||||
override var readMode: Boolean = false
|
||||
|
||||
override fun readMode() {
|
||||
readMode = true
|
||||
}
|
||||
|
||||
override fun resumeWriteMode(usedSize: Int) {
|
||||
readPosition += usedSize
|
||||
readMode = false
|
||||
}
|
||||
|
||||
override fun writeTo(os: OutputStream): Int {
|
||||
val size = readableSize
|
||||
os.write(array, readOffset, size)
|
||||
reset()
|
||||
return size
|
||||
}
|
||||
|
||||
|
||||
override val readOffset get() = offset + readPosition
|
||||
override val writeOffset get() = offset + writePosition
|
||||
|
||||
val readByteBuffer get() = HeapByteBuffer.wrap(array, offset + readPosition, writePosition - readPosition)
|
||||
val writeByteBuffer get() = HeapByteBuffer.wrap(array, offset + writePosition, limit - writePosition)
|
||||
|
||||
override val readableSize get() = writePosition - readPosition
|
||||
|
||||
val position get() = "ArrayByteBuffer(size=$size, writePosition=$writePosition, readPosition=$readPosition)"
|
||||
|
||||
/*
|
||||
* 位置控制方法
|
||||
*/
|
||||
|
||||
override fun clear() {
|
||||
writePosition = 0
|
||||
readPosition = 0
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
array.copyInto(array, offset, readOffset, offset + writePosition)
|
||||
writePosition = readableSize
|
||||
readPosition = 0
|
||||
}
|
||||
|
||||
override fun reset(outputStream: OutputStream) {
|
||||
outputStream.write(array, readOffset, offset + writePosition)
|
||||
writePosition = 0
|
||||
readPosition = 0
|
||||
}
|
||||
|
||||
override fun needReadSize(size: Int) {
|
||||
if (readableSize < size) throw OutOfBufferException()
|
||||
}
|
||||
|
||||
override fun take(size: Int): Int {
|
||||
needReadSize(size)
|
||||
val offset = readOffset
|
||||
readPosition += size
|
||||
return offset
|
||||
}
|
||||
|
||||
override fun useReadSize(size: Int): Int {
|
||||
needReadSize(size)
|
||||
readPosition += size
|
||||
return size
|
||||
}
|
||||
|
||||
override fun push(size: Int): Int {
|
||||
val offset = writeOffset
|
||||
writePosition += size
|
||||
return offset
|
||||
}
|
||||
|
||||
override fun readAllSize() = useReadSize(readableSize)
|
||||
override fun takeAll() = take(readableSize)
|
||||
|
||||
|
||||
/*
|
||||
* 数据获取方法
|
||||
*/
|
||||
|
||||
override fun get() = array[take(1)]
|
||||
override fun getChar() = array.toChar(take(2))
|
||||
override fun getShort() = array.toShort(take(2))
|
||||
override fun getInt() = array.toInt(take(4))
|
||||
override fun getLong() = array.toLong(take(8))
|
||||
override fun getFloat() = array.toFloat(take(4))
|
||||
override fun getDouble() = array.toDouble(take(8))
|
||||
override fun getBytes(size: Int): ByteArray {
|
||||
val readMode = readMode
|
||||
readMode()
|
||||
val array = array.copyOfRange(readPosition, useReadSize(size))
|
||||
if (!readMode) resumeWriteMode(size)
|
||||
return array
|
||||
}
|
||||
|
||||
override fun getString(size: Int) = String(array, readPosition, useReadSize(size))
|
||||
|
||||
override fun writeTo(buffer: ByteArray, bufferOffset: Int, size: Int): Int {
|
||||
array.copyInto(buffer, bufferOffset, offset, useReadSize(size))
|
||||
return size
|
||||
}
|
||||
|
||||
override fun toByteArray() = getBytes()
|
||||
|
||||
|
||||
/*
|
||||
* 数据写入方法
|
||||
*/
|
||||
|
||||
override fun put(byte: Byte) {
|
||||
array.put(byte, push(1))
|
||||
}
|
||||
|
||||
override fun put(char: Char) = array.put(char, push(2))
|
||||
override fun put(short: Short) = array.put(short, push(2))
|
||||
override fun put(int: Int) = array.put(int, push(4))
|
||||
override fun put(long: Long) = array.put(long, push(8))
|
||||
override fun put(float: Float) = array.put(float, push(4))
|
||||
override fun put(double: Double) = array.put(double, push(8))
|
||||
override fun put(str: String) = put(str.toByteArray())
|
||||
override fun put(byteArray: ByteArray, startIndex: Int, endIndex: Int) {
|
||||
byteArray.copyInto(array, push(endIndex - startIndex), startIndex, endIndex)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
//return String(array, readOffset, readableSize)
|
||||
return "ByteArrayAdvanceByteBuffer(size=$size, readPosition=$readPosition, writePosition=$writePosition)"
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓冲区用完异常
|
||||
*/
|
||||
class OutOfBufferException : Exception()
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package cn.tursom.core.bytebuffer
|
||||
|
||||
import cn.tursom.core.logE
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class DirectNioAdvanceByteBuffer(val buffer: ByteBuffer) : AdvanceByteBuffer {
|
||||
override val nioBuffer: ByteBuffer get() = buffer
|
||||
override val readOnly: Boolean get() = buffer.isReadOnly
|
||||
var writeMark = 0
|
||||
override var writePosition: Int
|
||||
get() {
|
||||
return if (readMode) writeMark
|
||||
else buffer.position()
|
||||
}
|
||||
set(value) {
|
||||
if (!readMode) buffer.position(value)
|
||||
else buffer.limit(value)
|
||||
}
|
||||
override var limit: Int = buffer.limit()
|
||||
get() = if (!readMode) buffer.limit() else field
|
||||
set(value) {
|
||||
if (!readMode) buffer.limit(value)
|
||||
field = value
|
||||
}
|
||||
override val capacity: Int get() = buffer.capacity()
|
||||
|
||||
override val hasArray: Boolean get() = false
|
||||
override val array: ByteArray get() = buffer.array()
|
||||
override val arrayOffset: Int = 0
|
||||
override var readPosition: Int = 0
|
||||
get() = if (readMode) buffer.position() else field
|
||||
set(value) {
|
||||
if (readMode) buffer.position(value)
|
||||
field = value
|
||||
}
|
||||
override val readableSize: Int get() = if (readMode) buffer.remaining() else writePosition - readPosition
|
||||
override val size: Int get() = buffer.capacity()
|
||||
override var readMode: Boolean = false
|
||||
|
||||
override fun readMode() {
|
||||
if (!readMode) {
|
||||
writeMark = writePosition
|
||||
//logE("readMode() $this $writeMark $writePosition ${buffer.position()}")
|
||||
readMode = true
|
||||
buffer.flip()
|
||||
buffer.position(readPosition)
|
||||
//logE("readMode() $this $writeMark $writePosition ${buffer.position()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun resumeWriteMode(usedSize: Int) {
|
||||
if (readMode) {
|
||||
readMode = false
|
||||
buffer.limit(capacity)
|
||||
buffer.position(writeMark)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
resumeWriteMode()
|
||||
buffer.clear()
|
||||
readPosition = 0
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "DirectNioAdvanceByteBuffer(buffer=$buffer, readMode=$readMode, readPosition=$readPosition, writePosition=$writePosition)"
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package cn.tursom.core.bytebuffer
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
/**
|
||||
* HOOK java.nio.HeapByteBuffer
|
||||
*/
|
||||
object HeapByteBuffer {
|
||||
private val field = ByteBuffer::class.java.getDeclaredField("offset")
|
||||
|
||||
init {
|
||||
field.isAccessible = true
|
||||
}
|
||||
|
||||
fun wrap(array: ByteArray, offset: Int = 0, size: Int = array.size - offset): ByteBuffer {
|
||||
val buffer = ByteBuffer.wrap(array, 0, size + offset)
|
||||
//return if (offset == 0) buffer else {
|
||||
// buffer.position(offset)
|
||||
// buffer.slice()
|
||||
//}
|
||||
if (offset > 0) field.set(buffer, offset)
|
||||
return buffer
|
||||
}
|
||||
|
||||
fun wrap(string: String) = wrap(string.toByteArray())
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
package cn.tursom.core.bytebuffer
|
||||
|
||||
import cn.tursom.core.*
|
||||
import java.io.OutputStream
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
class HeapNioAdvanceByteBuffer(val buffer: ByteBuffer) : AdvanceByteBuffer {
|
||||
constructor(size: Int) : this(ByteBuffer.allocate(size))
|
||||
constructor(buffer: ByteArray, offset: Int = 0, size: Int = buffer.size - offset) : this(HeapByteBuffer.wrap(buffer, offset, size))
|
||||
|
||||
override val nioBuffer: ByteBuffer get() = buffer
|
||||
|
||||
override val hasArray: Boolean get() = buffer.hasArray()
|
||||
override val readOnly: Boolean get() = buffer.isReadOnly
|
||||
|
||||
private var _readMode = false
|
||||
var readMark = 0
|
||||
var writeMark = 0
|
||||
|
||||
/**
|
||||
* 各种位置变量
|
||||
*/
|
||||
override var writePosition
|
||||
get() = buffer.position()
|
||||
set(value) {
|
||||
buffer.position(value)
|
||||
}
|
||||
override var limit
|
||||
get() = buffer.limit()
|
||||
set(value) {
|
||||
buffer.limit(value)
|
||||
}
|
||||
|
||||
override val capacity: Int = buffer.capacity()
|
||||
override val array: ByteArray get() = buffer.array()
|
||||
override val arrayOffset: Int get() = buffer.arrayOffset()
|
||||
override var readPosition: Int = 0
|
||||
override val readOffset get() = arrayOffset + readPosition
|
||||
override val readableSize get() = writePosition - readPosition
|
||||
override val available get() = readableSize
|
||||
override val writeOffset get() = arrayOffset + writePosition
|
||||
override val writeableSize get() = limit - writePosition
|
||||
override val size = buffer.capacity()
|
||||
override val readMode get() = _readMode
|
||||
|
||||
/*
|
||||
* 位置控制方法
|
||||
*/
|
||||
|
||||
override fun readMode() {
|
||||
writeMark = buffer.position()
|
||||
readMark = readPosition
|
||||
buffer.limit(buffer.position())
|
||||
buffer.position(readPosition)
|
||||
_readMode = true
|
||||
}
|
||||
|
||||
override fun resumeWriteMode(usedSize: Int) {
|
||||
readPosition = readMark + usedSize
|
||||
buffer.limit(buffer.capacity())
|
||||
buffer.position(writeMark)
|
||||
_readMode = false
|
||||
}
|
||||
|
||||
override fun needReadSize(size: Int) {
|
||||
if (readableSize < size) throw OutOfBufferException()
|
||||
}
|
||||
|
||||
override fun useReadSize(size: Int): Int {
|
||||
needReadSize(size)
|
||||
readPosition += size
|
||||
return size
|
||||
}
|
||||
|
||||
override fun take(size: Int): Int {
|
||||
needReadSize(size)
|
||||
val offset = readOffset
|
||||
readPosition += size
|
||||
return offset
|
||||
}
|
||||
|
||||
override fun push(size: Int): Int {
|
||||
val offset = writeOffset
|
||||
writePosition += size
|
||||
return offset
|
||||
}
|
||||
|
||||
override fun readAllSize() = useReadSize(readableSize)
|
||||
override fun takeAll() = take(readableSize)
|
||||
|
||||
override fun clear() {
|
||||
readPosition = 0
|
||||
buffer.clear()
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
array.copyInto(array, arrayOffset, readOffset, arrayOffset + writePosition)
|
||||
writePosition = readableSize
|
||||
readPosition = 0
|
||||
}
|
||||
|
||||
override fun reset(outputStream: OutputStream) {
|
||||
outputStream.write(array, readOffset, arrayOffset + writePosition)
|
||||
writePosition = 0
|
||||
readPosition = 0
|
||||
}
|
||||
|
||||
override fun requireAvailableSize(size: Int) {
|
||||
if (limit - readPosition < size) reset()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 数据获取方法
|
||||
*/
|
||||
|
||||
override fun get() = array[take(1)]
|
||||
override fun getChar() = array.toChar(take(2))
|
||||
override fun getShort() = array.toShort(take(2))
|
||||
override fun getInt() = array.toInt(take(4))
|
||||
override fun getLong() = array.toLong(take(8))
|
||||
override fun getFloat() = array.toFloat(take(4))
|
||||
override fun getDouble() = array.toDouble(take(8))
|
||||
override fun getBytes(size: Int): ByteArray {
|
||||
val readMode = readMode
|
||||
readMode()
|
||||
val array = array.copyOfRange(readPosition, useReadSize(size))
|
||||
if (!readMode) resumeWriteMode(size)
|
||||
return array
|
||||
}
|
||||
|
||||
override fun getString(size: Int) = String(array, readOffset, useReadSize(size))
|
||||
|
||||
override fun writeTo(buffer: ByteArray, bufferOffset: Int, size: Int): Int {
|
||||
array.copyInto(buffer, bufferOffset, arrayOffset, useReadSize(size))
|
||||
return size
|
||||
}
|
||||
|
||||
override fun toByteArray() = getBytes()
|
||||
|
||||
|
||||
/*
|
||||
* 数据写入方法
|
||||
*/
|
||||
|
||||
override fun put(byte: Byte) {
|
||||
buffer.put(byte)
|
||||
}
|
||||
|
||||
override fun put(char: Char) = array.put(char, push(2))
|
||||
override fun put(short: Short) = array.put(short, push(2))
|
||||
override fun put(int: Int) = array.put(int, push(4))
|
||||
override fun put(long: Long) = array.put(long, push(8))
|
||||
override fun put(float: Float) = array.put(float, push(4))
|
||||
override fun put(double: Double) = array.put(double, push(8))
|
||||
override fun put(str: String) = put(str.toByteArray())
|
||||
override fun put(byteArray: ByteArray, startIndex: Int, endIndex: Int) {
|
||||
byteArray.copyInto(array, push(endIndex - startIndex), startIndex, endIndex)
|
||||
}
|
||||
|
||||
override fun split(from: Int, to: Int): AdvanceByteBuffer {
|
||||
val readMark = readPosition
|
||||
val writeMark = writePosition
|
||||
buffer.position(readMark)
|
||||
buffer.limit(writeMark)
|
||||
val slice = HeapNioAdvanceByteBuffer(buffer.slice())
|
||||
readPosition = readMark
|
||||
writePosition = writeMark
|
||||
return slice
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "HeapNioAdvanceByteBuffer(buffer=$buffer, readMode=$_readMode, readMark=$readMark, writeMark=$writeMark, capacity=$capacity, readPosition=$readPosition, size=$size)"
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
package cn.tursom.core.bytebuffer
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class MultiAdvanceByteBuffer(vararg val buffers: AdvanceByteBuffer) : AdvanceByteBuffer {
|
||||
init {
|
||||
resumeWriteMode()
|
||||
}
|
||||
|
||||
var writeBufferIndex = 0
|
||||
var readBufferIndex = 0
|
||||
val readBuffer get() = buffers[writeBufferIndex]
|
||||
val writeBuffer get() = buffers[writeBufferIndex]
|
||||
|
||||
val operatorBuffer
|
||||
get() = if (readMode) {
|
||||
readBuffer
|
||||
} else {
|
||||
writeBuffer
|
||||
}
|
||||
|
||||
override val nioBuffers: Array<out ByteBuffer>
|
||||
get() {
|
||||
val bufList = ArrayList<ByteBuffer>()
|
||||
buffers.forEach { buffer ->
|
||||
if (buffer.bufferCount == 1) {
|
||||
bufList.add(buffer.nioBuffer)
|
||||
} else {
|
||||
buffer.nioBuffers.forEach {
|
||||
bufList.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
return bufList.toTypedArray()
|
||||
}
|
||||
override val hasArray: Boolean get() = false
|
||||
override val readOnly: Boolean get() = false
|
||||
override val bufferCount: Int get() = buffers.size
|
||||
|
||||
override val nioBuffer: ByteBuffer get() = operatorBuffer.nioBuffer
|
||||
override var writePosition: Int
|
||||
get() = operatorBuffer.writePosition
|
||||
set(value) {
|
||||
operatorBuffer.writePosition = value
|
||||
}
|
||||
override var limit: Int
|
||||
get() = operatorBuffer.limit
|
||||
set(value) {
|
||||
operatorBuffer.limit = value
|
||||
}
|
||||
override val capacity: Int get() = operatorBuffer.capacity
|
||||
override val array: ByteArray get() = operatorBuffer.array
|
||||
override val arrayOffset: Int get() = operatorBuffer.arrayOffset
|
||||
override var readPosition: Int
|
||||
get() = operatorBuffer.readPosition
|
||||
set(value) {
|
||||
operatorBuffer.readPosition = value
|
||||
}
|
||||
override val readOffset: Int get() = operatorBuffer.readOffset
|
||||
override val readableSize: Int get() = operatorBuffer.readableSize
|
||||
override val available: Int get() = operatorBuffer.available
|
||||
override val writeOffset: Int get() = operatorBuffer.writeOffset
|
||||
override val writeableSize: Int get() = operatorBuffer.writeableSize
|
||||
override val size: Int get() = operatorBuffer.size
|
||||
override var readMode: Boolean = false
|
||||
|
||||
override fun readMode() {
|
||||
readMode = true
|
||||
buffers.forEach(AdvanceByteBuffer::readMode)
|
||||
}
|
||||
|
||||
override fun resumeWriteMode(usedSize: Int) {
|
||||
readMode = false
|
||||
buffers.forEach { it.resumeWriteMode() }
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
writeBufferIndex = 0
|
||||
readBufferIndex = 0
|
||||
buffers.forEach { buffer -> buffer.clear() }
|
||||
}
|
||||
|
||||
override fun get(): Byte = readBuffer.get()
|
||||
override fun getChar(): Char = readBuffer.getChar()
|
||||
override fun getShort(): Short = readBuffer.getShort()
|
||||
override fun getInt(): Int = readBuffer.getInt()
|
||||
override fun getLong(): Long = readBuffer.getLong()
|
||||
override fun getFloat(): Float = readBuffer.getFloat()
|
||||
override fun getDouble(): Double = readBuffer.getDouble()
|
||||
override fun getBytes(size: Int) = readBuffer.getBytes(size)
|
||||
override fun getString(size: Int): String = readBuffer.getString(size)
|
||||
|
||||
override fun put(byte: Byte) = writeBuffer.put(byte)
|
||||
override fun put(char: Char) = writeBuffer.put(char)
|
||||
override fun put(short: Short) = writeBuffer.put(short)
|
||||
override fun put(int: Int) = writeBuffer.put(int)
|
||||
override fun put(long: Long) = writeBuffer.put(long)
|
||||
override fun put(float: Float) = writeBuffer.put(float)
|
||||
override fun put(double: Double) = writeBuffer.put(double)
|
||||
override fun put(str: String) = writeBuffer.put(str)
|
||||
override fun toString(): String {
|
||||
return "MultiAdvanceByteBuffer(buffers=${Arrays.toString(buffers)}, writeBufferIndex=$writeBufferIndex, readBufferIndex=$readBufferIndex, readMode=$readMode)"
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package cn.tursom.core.bytebuffer
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class NioAdvanceByteBuffer(val buffer: ByteBuffer) :
|
||||
AdvanceByteBuffer by if (buffer.hasArray()) {
|
||||
HeapNioAdvanceByteBuffer(buffer)
|
||||
} else {
|
||||
DirectNioAdvanceByteBuffer(buffer)
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package cn.tursom.core.bytebuffer
|
||||
|
||||
/**
|
||||
* 缓冲区用完异常
|
||||
*/
|
||||
class OutOfBufferException : Exception()
|
62
src/main/kotlin/cn/tursom/core/pool/AbstractMemoryPool.kt
Normal file
62
src/main/kotlin/cn/tursom/core/pool/AbstractMemoryPool.kt
Normal file
@ -0,0 +1,62 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
import cn.tursom.core.buffer.impl.PooledByteBuffer
|
||||
import cn.tursom.core.AtomicBitSet
|
||||
|
||||
abstract class AbstractMemoryPool(
|
||||
val blockSize: Int,
|
||||
val blockCount: Int,
|
||||
val emptyPoolBuffer: (blockSize: Int) -> ByteBuffer = { HeapByteBuffer(blockSize) },
|
||||
private val memoryPool: ByteBuffer
|
||||
) : MemoryPool {
|
||||
private val bitMap = AtomicBitSet(blockCount.toLong())
|
||||
val allocated: Int get() = bitMap.trueCount.toInt()
|
||||
|
||||
private fun getMemory(token: Int): ByteBuffer = synchronized(this) {
|
||||
PooledByteBuffer(memoryPool.slice(token * blockSize, blockSize), this, token)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token
|
||||
*/
|
||||
private fun allocate(): Int {
|
||||
var index = bitMap.firstDown()
|
||||
while (index in 0 until blockCount) {
|
||||
if (bitMap.up(index)) {
|
||||
return index.toInt()
|
||||
}
|
||||
index = if (bitMap[index]) bitMap.firstDown() else index
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
override fun free(memory: ByteBuffer) {
|
||||
if (memory is PooledByteBuffer && memory.pool == this) {
|
||||
val token = memory.token
|
||||
@Suppress("ControlFlowWithEmptyBody")
|
||||
if (token in 0 until blockCount) while (!bitMap.down(token.toLong()));
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemoryOrNull(): ByteBuffer? {
|
||||
val token = allocate()
|
||||
return if (token in 0 until blockCount) {
|
||||
return getMemory(token)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemory(): ByteBuffer = getMemoryOrNull() ?: emptyPoolBuffer(blockSize)
|
||||
|
||||
override fun get(blockCount: Int): Array<ByteBuffer> = Array(blockCount) {
|
||||
val token = allocate()
|
||||
if (token in 0 until blockCount) {
|
||||
getMemory(token)
|
||||
} else {
|
||||
emptyPoolBuffer(blockSize)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.datastruct.ArrayBitSet
|
||||
|
||||
class ArrayPool<T> : Pool<T> {
|
||||
private val bitSet = ArrayBitSet(64)
|
||||
private var poll = Array<Any?>(bitSet.size.toInt()) { null }
|
||||
|
||||
override fun put(cache: T): Boolean {
|
||||
val index = synchronized(bitSet) {
|
||||
val index = getDown()
|
||||
bitSet.up(index)
|
||||
index
|
||||
}.toInt()
|
||||
poll[index] = cache
|
||||
return true
|
||||
}
|
||||
|
||||
override fun get(): T? = synchronized(bitSet) {
|
||||
val index = bitSet.firstUp()
|
||||
if (index < 0) null
|
||||
else {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val ret = poll[index.toInt()] as T
|
||||
bitSet.down(index)
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDown(): Long = synchronized(bitSet) {
|
||||
val index = bitSet.firstDown()
|
||||
if (index < 0) {
|
||||
resize()
|
||||
bitSet.firstDown()
|
||||
} else {
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
private fun resize() {
|
||||
bitSet.resize(bitSet.size * 2)
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
interface AsyncPool<T> {
|
||||
suspend fun put(cache: T): Boolean
|
||||
suspend fun get(): T?
|
||||
|
||||
suspend fun <T> AsyncPool<T>.forceGet(): T {
|
||||
return get() ?: throw Pool.NoCacheException()
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
class CachedLinkedPool<T> : Pool<T> {
|
||||
@Volatile
|
||||
private var rootNode: Node<T>? = null
|
||||
@Volatile
|
||||
private var cacheNode: Node<T>? = null
|
||||
|
||||
override fun put(cache: T): Boolean {
|
||||
synchronized(this) {
|
||||
rootNode = if (cacheNode != null) {
|
||||
val node = cacheNode!!
|
||||
cacheNode = node.next
|
||||
node.next = rootNode
|
||||
rootNode = node
|
||||
node
|
||||
} else {
|
||||
Node(cache, rootNode)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun get(): T? {
|
||||
synchronized(this) {
|
||||
val node = rootNode
|
||||
rootNode = rootNode?.next
|
||||
val value = node?.value
|
||||
node?.value = null
|
||||
node?.next = cacheNode
|
||||
cacheNode = node
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
private class Node<T>(var value: T? = null, var next: Node<T>? = null)
|
||||
}
|
@ -1,54 +1,21 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.NioAdvanceByteBuffer
|
||||
import cn.tursom.core.datastruct.ArrayBitSet
|
||||
import java.nio.ByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.DirectByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
|
||||
class DirectMemoryPool(override val blockSize: Int = 1024, override val blockCount: Int = 16) : MemoryPool {
|
||||
private val memoryPool = ByteBuffer.allocateDirect(blockSize * blockCount)
|
||||
private val bitMap = ArrayBitSet(blockCount.toLong())
|
||||
|
||||
/**
|
||||
* @return token
|
||||
*/
|
||||
override fun allocate(): Int = synchronized(this) {
|
||||
val index = bitMap.firstDown()
|
||||
if (index in 0 until blockCount) {
|
||||
bitMap.up(index)
|
||||
index.toInt()
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
override fun free(token: Int) {
|
||||
if (token in 0 until blockCount) synchronized(this) {
|
||||
bitMap.down(token.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemory(token: Int): ByteBuffer? = if (token in 0 until blockCount) {
|
||||
synchronized(this) {
|
||||
memoryPool.limit((token + 1) * blockSize)
|
||||
memoryPool.position(token * blockSize)
|
||||
return memoryPool.slice()
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
override fun getAdvanceByteBuffer(token: Int): AdvanceByteBuffer? = if (token in 0 until blockCount) {
|
||||
synchronized(this) {
|
||||
memoryPool.limit((token + 1) * blockSize)
|
||||
memoryPool.position(token * blockSize)
|
||||
return NioAdvanceByteBuffer(memoryPool.slice())
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
class DirectMemoryPool(
|
||||
blockSize: Int = 1024,
|
||||
blockCount: Int = 16,
|
||||
emptyPoolBuffer: (blockSize: Int) -> ByteBuffer = { HeapByteBuffer(it) }
|
||||
) : AbstractMemoryPool(
|
||||
blockSize,
|
||||
blockCount,
|
||||
emptyPoolBuffer,
|
||||
DirectByteBuffer(java.nio.ByteBuffer.allocateDirect(blockSize * blockCount))
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "DirectMemoryPool(blockSize=$blockSize, blockCount=$blockCount, bitMap=$bitMap)"
|
||||
return "DirectMemoryPool(blockSize=$blockSize, blockCount=$blockCount, allocated=$allocated)"
|
||||
}
|
||||
}
|
69
src/main/kotlin/cn/tursom/core/pool/ExpandableMemoryPool.kt
Normal file
69
src/main/kotlin/cn/tursom/core/pool/ExpandableMemoryPool.kt
Normal file
@ -0,0 +1,69 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* 可自动申请新内存空间的内存池
|
||||
* 线程安全
|
||||
*/
|
||||
class ExpandableMemoryPool(private val poolFactory: () -> MemoryPool) : MemoryPool {
|
||||
private val poolList = ConcurrentLinkedQueue<MemoryPool>()
|
||||
@Volatile
|
||||
private var usingPool: MemoryPool
|
||||
private val poolLock = AtomicBoolean(false)
|
||||
|
||||
init {
|
||||
usingPool = poolFactory()
|
||||
poolList.add(usingPool)
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制释放所有内存
|
||||
*/
|
||||
override fun gc() {
|
||||
poolList.clear()
|
||||
usingPool = poolFactory()
|
||||
poolList.add(usingPool)
|
||||
}
|
||||
|
||||
override fun free(memory: ByteBuffer) = throw NotImplementedError("ExpandableMemoryPool won't allocate any memory")
|
||||
|
||||
override fun getMemory(): ByteBuffer {
|
||||
var buffer = usingPool.getMemoryOrNull()
|
||||
if (buffer != null) return buffer
|
||||
poolList.forEach {
|
||||
val pool = it ?: return@forEach
|
||||
buffer = pool.getMemoryOrNull()
|
||||
if (buffer != null) usingPool = pool
|
||||
return buffer ?: return@forEach
|
||||
}
|
||||
return newPool()
|
||||
}
|
||||
|
||||
override fun getMemoryOrNull(): ByteBuffer = getMemory()
|
||||
|
||||
override fun toString(): String {
|
||||
return "ExpandableMemoryPool(poolList=$poolList, usingPool=$usingPool)"
|
||||
}
|
||||
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
private fun newPool(): ByteBuffer {
|
||||
return if (poolLock.compareAndSet(false, true)) {
|
||||
val newPool = poolFactory()
|
||||
poolList.add(newPool)
|
||||
poolLock.set(false)
|
||||
usingPool = newPool
|
||||
synchronized(poolLock) {
|
||||
(poolLock as Object).notifyAll()
|
||||
}
|
||||
newPool.getMemory()
|
||||
} else {
|
||||
synchronized(poolLock) {
|
||||
(poolLock as Object).wait(500)
|
||||
}
|
||||
poolList.last().getMemory()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +1,21 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.NioAdvanceByteBuffer
|
||||
import cn.tursom.core.datastruct.ArrayBitSet
|
||||
import java.nio.ByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class HeapMemoryPool(override val blockSize: Int = 1024, override val blockCount: Int = 16) : MemoryPool {
|
||||
private val memoryPool = ByteBuffer.allocate(blockSize * blockCount)
|
||||
private val bitMap = ArrayBitSet(blockCount.toLong())
|
||||
|
||||
/**
|
||||
* @return token
|
||||
*/
|
||||
override fun allocate(): Int = synchronized(this) {
|
||||
val index = bitMap.firstDown()
|
||||
if (index in 0 until blockCount) {
|
||||
bitMap.up(index)
|
||||
index.toInt()
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
override fun free(token: Int) {
|
||||
if (token in 0 until blockCount) synchronized(this) {
|
||||
bitMap.down(token.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemory(token: Int): ByteBuffer? = if (token in 0 until blockCount) {
|
||||
synchronized(this) {
|
||||
memoryPool.limit((token + 1) * blockSize)
|
||||
memoryPool.position(token * blockSize)
|
||||
return memoryPool.slice()
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
override fun getAdvanceByteBuffer(token: Int): AdvanceByteBuffer? = if (token in 0 until blockCount) {
|
||||
synchronized(this) {
|
||||
memoryPool.limit((token + 1) * blockSize)
|
||||
memoryPool.position(token * blockSize)
|
||||
return NioAdvanceByteBuffer(memoryPool.slice())
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "HeapMemoryPool(blockSize=$blockSize, blockCount=$blockCount, bitMap=$bitMap)"
|
||||
}
|
||||
class HeapMemoryPool(
|
||||
blockSize: Int = 1024,
|
||||
blockCount: Int = 16,
|
||||
emptyPoolBuffer: (blockSize: Int) -> ByteBuffer = { HeapByteBuffer(it) }
|
||||
) : AbstractMemoryPool(
|
||||
blockSize,
|
||||
blockCount,
|
||||
emptyPoolBuffer,
|
||||
HeapByteBuffer(java.nio.ByteBuffer.allocate(blockSize * blockCount))
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "HeapMemoryPool(blockSize=$blockSize, blockCount=$blockCount, allocated=$allocated)"
|
||||
}
|
||||
}
|
34
src/main/kotlin/cn/tursom/core/pool/InstantMemoryPool.kt
Normal file
34
src/main/kotlin/cn/tursom/core/pool/InstantMemoryPool.kt
Normal file
@ -0,0 +1,34 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
import cn.tursom.core.buffer.impl.InstantByteBuffer
|
||||
import java.lang.ref.SoftReference
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
class InstantMemoryPool(
|
||||
val blockSize: Int,
|
||||
val newMemory: (blockSize: Int) -> ByteBuffer = { HeapByteBuffer(blockSize) }
|
||||
) : MemoryPool {
|
||||
private val memoryList = ConcurrentLinkedQueue<SoftReference<InstantByteBuffer>>()
|
||||
|
||||
override fun free(memory: ByteBuffer) {
|
||||
if (memory is InstantByteBuffer && memory.pool == this && !memory.closed) {
|
||||
memory.closed = true
|
||||
memoryList.add(SoftReference(memory))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemory(): ByteBuffer = getMemoryOrNull() ?: InstantByteBuffer(newMemory(blockSize), this)
|
||||
|
||||
override fun getMemoryOrNull(): ByteBuffer? {
|
||||
memoryList.forEach {
|
||||
return it.get() ?: return@forEach
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "InstantMemoryPool(blockSize=$blockSize, memoryList=${memoryList.size})"
|
||||
}
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
class LinkedPool<T> : Pool<T> {
|
||||
@Volatile
|
||||
private var rootNode: Node<T>? = null
|
||||
|
||||
override fun put(cache: T): Boolean {
|
||||
synchronized(this) {
|
||||
rootNode = Node(cache, rootNode)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun get(): T? {
|
||||
synchronized(this) {
|
||||
val node = rootNode
|
||||
rootNode = rootNode?.next
|
||||
return node?.value
|
||||
}
|
||||
}
|
||||
|
||||
private class Node<T>(var value: T? = null, var next: Node<T>? = null)
|
||||
@Volatile
|
||||
private var rootNode: Node<T>? = null
|
||||
|
||||
override fun put(cache: T): Boolean {
|
||||
synchronized(this) {
|
||||
rootNode = Node(cache, rootNode)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun get(): T? {
|
||||
synchronized(this) {
|
||||
val node = rootNode
|
||||
rootNode = rootNode?.next
|
||||
return node?.value
|
||||
}
|
||||
}
|
||||
|
||||
private class Node<T>(var value: T? = null, var next: Node<T>? = null)
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.LongBitSet
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
import cn.tursom.core.buffer.impl.PooledByteBuffer
|
||||
|
||||
/**
|
||||
* 无锁,固定容量的内存池
|
||||
*/
|
||||
abstract class LongBitSetAbstractMemoryPool(
|
||||
val blockSize: Int,
|
||||
val emptyPoolBuffer: (blockSize: Int) -> ByteBuffer = { HeapByteBuffer(blockSize) },
|
||||
private val memoryPool: ByteBuffer
|
||||
) : MemoryPool {
|
||||
private val bitMap = LongBitSet()
|
||||
val allocated: Int get() = bitMap.trueCount.toInt()
|
||||
val blockCount: Int get() = 64
|
||||
|
||||
init {
|
||||
assert(memoryPool.capacity >= 64 * blockSize)
|
||||
}
|
||||
|
||||
private fun getMemory(token: Int): ByteBuffer = synchronized(this) {
|
||||
return PooledByteBuffer(memoryPool.slice(token * blockSize, blockSize), this, token)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token
|
||||
*/
|
||||
private fun allocate(): Int {
|
||||
var index = bitMap.firstDown()
|
||||
while (index >= 0) {
|
||||
if (bitMap.up(index)) {
|
||||
return index
|
||||
}
|
||||
index = bitMap.firstDown()
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
override fun free(memory: ByteBuffer) {
|
||||
if (memory is PooledByteBuffer && memory.pool == this) {
|
||||
val token = memory.token
|
||||
@Suppress("ControlFlowWithEmptyBody")
|
||||
if (token >= 0) while (!bitMap.down(token));
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemoryOrNull(): ByteBuffer? {
|
||||
val token = allocate()
|
||||
return if (token >= 0) {
|
||||
return getMemory(token)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemory(): ByteBuffer = getMemoryOrNull() ?: emptyPoolBuffer(blockSize)
|
||||
|
||||
override fun get(blockCount: Int): Array<ByteBuffer> = Array(blockCount) {
|
||||
val token = allocate()
|
||||
if (token in 0 until blockCount) {
|
||||
getMemory(token)
|
||||
} else {
|
||||
emptyPoolBuffer(blockSize)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.DirectByteBuffer
|
||||
|
||||
class LongBitSetDirectMemoryPool(
|
||||
blockSize: Int,
|
||||
emptyPoolBuffer: (blockSize: Int) -> ByteBuffer = { DirectByteBuffer(it) }
|
||||
) : LongBitSetAbstractMemoryPool(blockSize, emptyPoolBuffer, DirectByteBuffer(64 * blockSize)) {
|
||||
override fun toString(): String {
|
||||
return "LongBitSetDirectMemoryPool(blockSize=$blockSize, blockCount=$blockCount, allocated=$allocated)"
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
|
||||
class LongBitSetHeapMemoryPool (
|
||||
blockSize: Int,
|
||||
emptyPoolBuffer: (blockSize: Int) -> ByteBuffer = { HeapByteBuffer(it) }
|
||||
) : LongBitSetAbstractMemoryPool(blockSize, emptyPoolBuffer, HeapByteBuffer(64 * blockSize)) {
|
||||
override fun toString(): String {
|
||||
return "LongBitSetDirectMemoryPool(blockSize=$blockSize, blockCount=$blockCount, allocated=$allocated)"
|
||||
}
|
||||
}
|
47
src/main/kotlin/cn/tursom/core/pool/MarkedMemoryPool.kt
Normal file
47
src/main/kotlin/cn/tursom/core/pool/MarkedMemoryPool.kt
Normal file
@ -0,0 +1,47 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.PooledByteBuffer
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
* 可以记录与释放分配内存的内存池
|
||||
* 在被垃圾回收时能自动回收内存池的内存
|
||||
* 是一次性用品
|
||||
* 非线程安全
|
||||
*/
|
||||
class MarkedMemoryPool(private val pool: MemoryPool) : MemoryPool by pool, Closeable {
|
||||
private val allocatedList = ArrayList<ByteBuffer>(2)
|
||||
override fun getMemory(): ByteBuffer {
|
||||
val memory = pool.getMemory()
|
||||
allocatedList.add(memory)
|
||||
return memory
|
||||
}
|
||||
|
||||
override fun getMemoryOrNull(): ByteBuffer? {
|
||||
val memory = pool.getMemoryOrNull()
|
||||
if (memory != null) allocatedList.add(memory)
|
||||
return memory
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
allocatedList.forEach(ByteBuffer::close)
|
||||
allocatedList.clear()
|
||||
}
|
||||
|
||||
override fun gc() {
|
||||
pool.gc()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val allocated = ArrayList<Int>(allocatedList.size)
|
||||
allocatedList.forEach {
|
||||
if (it is PooledByteBuffer && !it.closed) allocated.add(it.token)
|
||||
}
|
||||
return "MarkedMemoryPool(pool=$pool, allocated=$allocated)"
|
||||
}
|
||||
|
||||
protected fun finalize() {
|
||||
close()
|
||||
}
|
||||
}
|
@ -1,47 +1,30 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.bytebuffer.NioAdvanceByteBuffer
|
||||
import java.nio.ByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
|
||||
/**
|
||||
* 内存池,提供批量的等大小的 ByteBuffer
|
||||
* 使用 allocate 分配内存,使用 getMemory 或 getAdvanceByteBuffer 获得内存,使用 free 释放内存
|
||||
* 内存池
|
||||
*/
|
||||
interface MemoryPool {
|
||||
val blockSize: Int
|
||||
val blockCount: Int
|
||||
|
||||
fun allocate(): Int
|
||||
fun free(token: Int)
|
||||
fun getMemory(token: Int): ByteBuffer?
|
||||
fun getAdvanceByteBuffer(token: Int): AdvanceByteBuffer? {
|
||||
val buffer = getMemory(token)
|
||||
return if (buffer != null) {
|
||||
NioAdvanceByteBuffer(buffer)
|
||||
} else {
|
||||
null
|
||||
val staticSize: Boolean get() = true
|
||||
|
||||
// fun allocate(): Int
|
||||
fun free(memory: ByteBuffer)
|
||||
|
||||
fun getMemory(): ByteBuffer
|
||||
fun getMemoryOrNull(): ByteBuffer?
|
||||
|
||||
override fun toString(): String
|
||||
|
||||
suspend operator fun <T> invoke(action: suspend (ByteBuffer) -> T): T {
|
||||
return getMemory().use { buffer ->
|
||||
action(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String
|
||||
|
||||
fun get() = getMemory()
|
||||
|
||||
operator fun get(blockCount: Int): Array<ByteBuffer> = Array(blockCount) { get() }
|
||||
|
||||
fun gc() {}
|
||||
}
|
||||
|
||||
|
||||
inline fun <T> MemoryPool.usingMemory(action: (ByteBuffer?) -> T): T {
|
||||
val token = allocate()
|
||||
return try {
|
||||
action(getMemory(token))
|
||||
} finally {
|
||||
free(token)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> MemoryPool.usingAdvanceByteBuffer(action: (AdvanceByteBuffer?) -> T): T {
|
||||
val token = allocate()
|
||||
return try {
|
||||
action(getAdvanceByteBuffer(token))
|
||||
} finally {
|
||||
free(token)
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
interface Pool<T> {
|
||||
fun put(cache: T): Boolean
|
||||
fun get(): T?
|
||||
|
||||
class NoCacheException : Exception()
|
||||
|
||||
fun <T> Pool<T>.forceGet(): T {
|
||||
return get() ?: throw NoCacheException()
|
||||
}
|
||||
}
|
||||
fun put(cache: T): Boolean
|
||||
fun get(): T?
|
||||
|
||||
class NoCacheException : Exception()
|
||||
|
||||
fun <T> Pool<T>.forceGet(): T {
|
||||
return get() ?: throw NoCacheException()
|
||||
}
|
||||
}
|
76
src/main/kotlin/cn/tursom/core/pool/ScalabilityMemoryPool.kt
Normal file
76
src/main/kotlin/cn/tursom/core/pool/ScalabilityMemoryPool.kt
Normal file
@ -0,0 +1,76 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import java.lang.ref.SoftReference
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* 利用 GC 进行内存释放的内存池
|
||||
*/
|
||||
class ScalabilityMemoryPool(private val poolFactory: () -> MemoryPool) : MemoryPool {
|
||||
private val poolList = ConcurrentLinkedQueue<SoftReference<MemoryPool>>()
|
||||
@Volatile
|
||||
private var usingPool: MemoryPool
|
||||
private val poolLock = AtomicBoolean(false)
|
||||
|
||||
init {
|
||||
usingPool = poolFactory()
|
||||
poolList.add(SoftReference(usingPool))
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放所有被 gc 释放的内存
|
||||
*/
|
||||
override fun gc() {
|
||||
val iterator = poolList.iterator()
|
||||
iterator.forEach {
|
||||
if (it.get() == null) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun free(memory: ByteBuffer) = throw NotImplementedError("ExpandableMemoryPool won't allocate any memory")
|
||||
|
||||
override fun getMemory(): ByteBuffer {
|
||||
var buffer = usingPool.getMemoryOrNull()
|
||||
if (buffer != null) return buffer
|
||||
val iterator = poolList.iterator()
|
||||
iterator.forEach {
|
||||
val pool = it.get() ?: run {
|
||||
iterator.remove()
|
||||
return@forEach
|
||||
}
|
||||
buffer = pool.getMemoryOrNull()
|
||||
if (buffer != null) usingPool = pool
|
||||
return buffer ?: return@forEach
|
||||
}
|
||||
return newPool()
|
||||
}
|
||||
|
||||
override fun getMemoryOrNull(): ByteBuffer = getMemory()
|
||||
|
||||
override fun toString(): String {
|
||||
return "ScalabilityMemoryPool(poolList=$poolList, usingPool=$usingPool)"
|
||||
}
|
||||
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
private fun newPool(): ByteBuffer {
|
||||
return if (poolLock.compareAndSet(false, true)) {
|
||||
val newPool = poolFactory()
|
||||
poolList.add(SoftReference(newPool))
|
||||
poolLock.set(false)
|
||||
usingPool = newPool
|
||||
synchronized(poolLock) {
|
||||
(poolLock as Object).notifyAll()
|
||||
}
|
||||
newPool.getMemory()
|
||||
} else {
|
||||
synchronized(poolLock) {
|
||||
(poolLock as Object).wait(500)
|
||||
}
|
||||
usingPool.getMemory()
|
||||
}
|
||||
}
|
||||
}
|
30
src/main/kotlin/cn/tursom/core/pool/ThreadLocalMemoryPool.kt
Normal file
30
src/main/kotlin/cn/tursom/core/pool/ThreadLocalMemoryPool.kt
Normal file
@ -0,0 +1,30 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
|
||||
class ThreadLocalMemoryPool(private val poolFactory: () -> MemoryPool) : MemoryPool {
|
||||
private val threadLocal = ThreadLocal<MemoryPool>()
|
||||
|
||||
override fun free(memory: ByteBuffer) = throw NotImplementedError("ThreadLocalMemoryPool won't allocate any memory")
|
||||
|
||||
override fun getMemory(): ByteBuffer = getPool().getMemory()
|
||||
|
||||
override fun getMemoryOrNull(): ByteBuffer? = getPool().getMemoryOrNull()
|
||||
|
||||
override fun toString(): String {
|
||||
return "ThreadLocalMemoryPool(threadLocal=$threadLocal)"
|
||||
}
|
||||
|
||||
override fun gc() {
|
||||
threadLocal.get()?.gc()
|
||||
}
|
||||
|
||||
private fun getPool(): MemoryPool {
|
||||
var pool = threadLocal.get()
|
||||
if (pool == null) {
|
||||
pool = poolFactory()
|
||||
threadLocal.set(pool)
|
||||
}
|
||||
return pool
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
import cn.tursom.core.buffer.impl.PooledByteBuffer
|
||||
import cn.tursom.core.datastruct.ArrayBitSet
|
||||
|
||||
abstract class ThreadUnsafeAbstractMemoryPool(
|
||||
val blockSize: Int,
|
||||
val blockCount: Int,
|
||||
val emptyPoolBuffer: (blockSize: Int) -> ByteBuffer = { HeapByteBuffer(blockSize) },
|
||||
private val memoryPool: ByteBuffer
|
||||
) : MemoryPool {
|
||||
private val bitMap = ArrayBitSet(blockCount.toLong())
|
||||
val allocated: Int get() = bitMap.trueCount.toInt()
|
||||
|
||||
private fun unsafeAllocate(): Int {
|
||||
val index = bitMap.firstDown()
|
||||
return if (index in 0 until blockCount) {
|
||||
bitMap.up(index)
|
||||
index.toInt()
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
private fun unsafeFree(token: Int) {
|
||||
bitMap.down(token.toLong())
|
||||
}
|
||||
|
||||
private fun unsafeGetMemory(token: Int): ByteBuffer {
|
||||
return PooledByteBuffer(memoryPool.slice(token * blockSize, blockSize), this, token)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token
|
||||
*/
|
||||
private fun allocate(): Int = unsafeAllocate()
|
||||
|
||||
override fun free(memory: ByteBuffer) {
|
||||
if (memory is PooledByteBuffer && memory.pool == this) {
|
||||
val token = memory.token
|
||||
if (token in 0 until blockCount) unsafeFree(token)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemoryOrNull(): ByteBuffer? {
|
||||
val token = allocate()
|
||||
return if (token in 0 until blockCount) {
|
||||
unsafeGetMemory(token)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemory(): ByteBuffer = getMemoryOrNull() ?: emptyPoolBuffer(blockSize)
|
||||
|
||||
override fun get(blockCount: Int): Array<ByteBuffer> = synchronized(this) {
|
||||
Array(blockCount) {
|
||||
val token = unsafeAllocate()
|
||||
if (token in 0 until blockCount) {
|
||||
unsafeGetMemory(token)
|
||||
} else {
|
||||
emptyPoolBuffer(blockSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.DirectByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
|
||||
|
||||
class ThreadUnsafeDirectMemoryPool(
|
||||
blockSize: Int = 1024,
|
||||
blockCount: Int = 16,
|
||||
emptyPoolBuffer: (blockSize: Int) -> ByteBuffer = { HeapByteBuffer(it) }
|
||||
) : ThreadUnsafeAbstractMemoryPool(
|
||||
blockSize,
|
||||
blockCount,
|
||||
emptyPoolBuffer,
|
||||
DirectByteBuffer(java.nio.ByteBuffer.allocateDirect(blockSize * blockCount))
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "DirectMemoryPool(blockSize=$blockSize, blockCount=$blockCount, allocated=$allocated)"
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package cn.tursom.core.pool
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.buffer.impl.HeapByteBuffer
|
||||
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class ThreadUnsafeHeapMemoryPool(
|
||||
blockSize: Int = 1024,
|
||||
blockCount: Int = 16,
|
||||
emptyPoolBuffer: (blockSize: Int) -> ByteBuffer = { HeapByteBuffer(it) }
|
||||
) : ThreadUnsafeAbstractMemoryPool(
|
||||
blockSize,
|
||||
blockCount,
|
||||
emptyPoolBuffer,
|
||||
HeapByteBuffer(java.nio.ByteBuffer.allocate(blockSize * blockCount))
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "ThreadUnsafeHeapMemoryPool(blockSize=$blockSize, blockCount=$blockCount, allocated=$allocated)"
|
||||
}
|
||||
}
|
55
src/main/kotlin/cn/tursom/core/timer/NonLockTaskQueue.kt
Normal file
55
src/main/kotlin/cn/tursom/core/timer/NonLockTaskQueue.kt
Normal file
@ -0,0 +1,55 @@
|
||||
package cn.tursom.core.timer
|
||||
|
||||
import cn.tursom.core.CurrentTimeMillisClock
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
class NonLockTaskQueue : TaskQueue {
|
||||
private val root = AtomicReference<TaskListNode?>()
|
||||
|
||||
private fun add(taskNode: TaskListNode): TaskListNode {
|
||||
while (!root.compareAndSet(taskNode.next, taskNode)) {
|
||||
taskNode.next = root.get()
|
||||
}
|
||||
return taskNode
|
||||
}
|
||||
|
||||
override fun offer(task: () -> Unit, timeout: Long): TimerTask {
|
||||
return add(TaskListNode(timeout, task, CurrentTimeMillisClock.now, root.get()))
|
||||
}
|
||||
|
||||
override fun offer(task: TimerTask): TimerTask {
|
||||
if (task.canceled || task.isOutTime) return task
|
||||
return if (task is TaskListNode) {
|
||||
add(task)
|
||||
} else {
|
||||
add(TaskListNode(task.timeout, task.task, task.createTime, root.get()))
|
||||
}
|
||||
}
|
||||
|
||||
override fun take(): TimerTask? {
|
||||
var node = root.get()
|
||||
while (!root.compareAndSet(node, node?.next)) {
|
||||
node = root.get()
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
private class TaskListNode(
|
||||
override val timeout: Long,
|
||||
override val task: () -> Unit,
|
||||
override val createTime: Long,
|
||||
@Volatile var next: TaskListNode?
|
||||
) : TimerTask {
|
||||
@Volatile
|
||||
override var canceled: Boolean = false
|
||||
override val outTime: Long = super.outTime
|
||||
|
||||
override fun invoke() {
|
||||
task()
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
canceled = true
|
||||
}
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
package cn.tursom.core.timer
|
||||
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ThreadFactory
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class StaticWheelTimer(
|
||||
val tick: Long = 200,
|
||||
val wheelSize: Int = 512
|
||||
) : Timer {
|
||||
var closed = false
|
||||
val taskQueueArray = Array(wheelSize) { TaskQueue() }
|
||||
private var position = 0
|
||||
|
||||
override fun exec(timeout: Long, task: () -> Unit): TimerTask {
|
||||
val index = ((timeout / tick + position + if (timeout % tick == 0L) 0 else 1) % wheelSize).toInt()
|
||||
return taskQueueArray[index].offer(task, timeout)
|
||||
}
|
||||
|
||||
init {
|
||||
thread(isDaemon = true) {
|
||||
while (!closed) {
|
||||
position %= wheelSize
|
||||
|
||||
val newQueue = TaskQueue()
|
||||
val taskQueue = taskQueueArray[position]
|
||||
taskQueueArray[position] = newQueue
|
||||
|
||||
val time = System.currentTimeMillis()
|
||||
var node = taskQueue.root
|
||||
while (node != null) {
|
||||
node = if (node.isOutTime(time)) {
|
||||
if (!node.canceled) threadPool.execute(node.task)
|
||||
node.next
|
||||
} else {
|
||||
val next = node.next
|
||||
newQueue.offer(node)
|
||||
next
|
||||
}
|
||||
}
|
||||
|
||||
position++
|
||||
Thread.sleep(tick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TaskQueue {
|
||||
var root: TaskNode? = null
|
||||
|
||||
fun offer(task: () -> Unit, timeout: Long): TaskNode {
|
||||
synchronized(this) {
|
||||
val insert = TaskNode(timeout, task, root)
|
||||
root = insert
|
||||
return insert
|
||||
}
|
||||
}
|
||||
|
||||
fun offer(node: TaskNode): TaskNode {
|
||||
synchronized(this) {
|
||||
node.next = root
|
||||
root = node
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
inner class TaskNode(
|
||||
val timeout: Long,
|
||||
val task: () -> Unit,
|
||||
var next: TaskNode?,
|
||||
var canceled: Boolean = false
|
||||
) : TimerTask {
|
||||
val outTime = System.currentTimeMillis() + timeout
|
||||
val isOutTime get() = System.currentTimeMillis() > outTime
|
||||
|
||||
fun isOutTime(time: Long) = time > outTime
|
||||
|
||||
override fun run() = task()
|
||||
|
||||
override fun cancel() {
|
||||
canceled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val threadPool: ExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(),
|
||||
object : ThreadFactory {
|
||||
var threadNumber = 0
|
||||
override fun newThread(r: Runnable): Thread {
|
||||
val thread = Thread(r)
|
||||
thread.isDaemon = true
|
||||
thread.name = "staticWheelTimerWorker-$threadNumber"
|
||||
return thread
|
||||
}
|
||||
})
|
||||
val timer by lazy { StaticWheelTimer(100, 1024) }
|
||||
val smoothTimer by lazy { StaticWheelTimer(20, 128) }
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package cn.tursom.core.timer
|
||||
|
||||
import cn.tursom.core.CurrentTimeMillisClock
|
||||
|
||||
class SynchronizedTaskQueue : TaskQueue {
|
||||
val root: TaskNode = TaskNode(0, {}, null, null)
|
||||
|
||||
private fun offer(task: () -> Unit, timeout: Long, createTime: Long): TaskNode {
|
||||
synchronized(root) {
|
||||
val insert = TaskNode(timeout, task, root, root.next, createTime)
|
||||
root.next = insert
|
||||
insert.next?.prev = insert
|
||||
return insert
|
||||
}
|
||||
}
|
||||
|
||||
override fun offer(task: () -> Unit, timeout: Long): TaskNode {
|
||||
return offer(task, timeout, CurrentTimeMillisClock.now)
|
||||
}
|
||||
|
||||
override fun offer(task: TimerTask): TimerTask {
|
||||
return if (task is TaskNode) {
|
||||
synchronized(root) {
|
||||
task.next = root.next
|
||||
task.next = task
|
||||
task.next?.prev = task
|
||||
task
|
||||
}
|
||||
} else {
|
||||
offer(task.task, task.timeout, task.createTime)
|
||||
}
|
||||
}
|
||||
|
||||
override fun take(): TimerTask? {
|
||||
synchronized(root) {
|
||||
val node = root.next
|
||||
root.next = node?.next
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
inner class TaskNode(
|
||||
override val timeout: Long,
|
||||
override val task: () -> Unit,
|
||||
@Volatile var prev: TaskNode?,
|
||||
@Volatile var next: TaskNode?,
|
||||
override val createTime: Long = CurrentTimeMillisClock.now
|
||||
) : TimerTask {
|
||||
@Volatile
|
||||
override var canceled: Boolean = false
|
||||
get() = synchronized(root) {
|
||||
field
|
||||
}
|
||||
|
||||
override val outTime = createTime + timeout
|
||||
|
||||
override fun invoke() {
|
||||
task()
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
synchronized(root) {
|
||||
canceled = true
|
||||
prev?.next = next
|
||||
next?.prev = prev
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
src/main/kotlin/cn/tursom/core/timer/TaskQueue.kt
Normal file
7
src/main/kotlin/cn/tursom/core/timer/TaskQueue.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package cn.tursom.core.timer
|
||||
|
||||
interface TaskQueue {
|
||||
fun offer(task: () -> Unit, timeout: Long): TimerTask
|
||||
fun offer(task: TimerTask): TimerTask
|
||||
fun take(): TimerTask?
|
||||
}
|
@ -1,5 +1,41 @@
|
||||
package cn.tursom.core.timer
|
||||
|
||||
import java.util.concurrent.*
|
||||
|
||||
interface Timer {
|
||||
fun exec(timeout: Long, task: () -> Unit): TimerTask
|
||||
fun exec(timeout: Long, task: () -> Unit): TimerTask
|
||||
|
||||
fun runNow(task: () -> Unit) {
|
||||
threadPool.execute(task)
|
||||
}
|
||||
|
||||
fun runNow(taskList: TaskQueue) {
|
||||
threadPool.execute {
|
||||
while (true) {
|
||||
val task = taskList.take() ?: return@execute
|
||||
try {
|
||||
task()
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val threadPool: ExecutorService = ThreadPoolExecutor(
|
||||
Runtime.getRuntime().availableProcessors(),
|
||||
Runtime.getRuntime().availableProcessors(),
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
LinkedTransferQueue(),
|
||||
object : ThreadFactory {
|
||||
var threadNumber = 0
|
||||
override fun newThread(r: Runnable): Thread {
|
||||
val thread = Thread(r)
|
||||
thread.isDaemon = true
|
||||
thread.name = "timer-worker-$threadNumber"
|
||||
return thread
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,15 @@
|
||||
package cn.tursom.core.timer
|
||||
|
||||
interface TimerTask {
|
||||
fun run()
|
||||
fun cancel()
|
||||
import cn.tursom.core.CurrentTimeMillisClock
|
||||
|
||||
interface TimerTask : () -> Unit {
|
||||
val canceled: Boolean
|
||||
val createTime: Long
|
||||
val timeout: Long
|
||||
val task: () -> Unit
|
||||
val outTime get() = createTime + timeout
|
||||
val isOutTime get() = CurrentTimeMillisClock.now > outTime
|
||||
|
||||
fun isOutTime(time: Long): Boolean = time > outTime
|
||||
fun cancel()
|
||||
}
|
@ -1,112 +1,93 @@
|
||||
package cn.tursom.core.timer
|
||||
|
||||
import cn.tursom.core.CurrentTimeMillisClock
|
||||
import java.lang.Thread.sleep
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ThreadFactory
|
||||
import java.util.concurrent.ScheduledFuture
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
||||
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate")
|
||||
class WheelTimer(
|
||||
val tick: Long = 200,
|
||||
val wheelSize: Int = 512
|
||||
val wheelSize: Int = 512,
|
||||
val name: String = "wheelTimerLooper",
|
||||
val taskQueueFactory: () -> TaskQueue = { NonLockTaskQueue() }
|
||||
) : Timer {
|
||||
var closed = false
|
||||
val taskQueueArray = Array(wheelSize) { TaskQueue() }
|
||||
val taskQueueArray = AtomicReferenceArray(Array(wheelSize) { taskQueueFactory() })
|
||||
@Volatile
|
||||
private var position = 0
|
||||
|
||||
|
||||
override fun exec(timeout: Long, task: () -> Unit): TimerTask {
|
||||
val index = ((timeout / tick + position + if (timeout % tick == 0L) 0 else 1) % wheelSize).toInt()
|
||||
//val index = ((timeout / tick + position + if (timeout % tick == 0L) 0 else 1) % wheelSize).toInt()
|
||||
val index = ((timeout / tick + position) % wheelSize).toInt()
|
||||
return taskQueueArray[index].offer(task, timeout)
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
thread(isDaemon = true, name = "wheelTimerLooper") {
|
||||
val startTime = System.currentTimeMillis()
|
||||
//ScheduledThreadPoolExecutor(1) { runnable ->
|
||||
// val thread = Thread(runnable, name)
|
||||
// thread.isDaemon = true
|
||||
// thread
|
||||
//}.scheduleAtFixedRate(tick, tick, TimeUnit.MILLISECONDS) {
|
||||
// val outTimeQueue = taskQueueFactory()
|
||||
// val newQueue = taskQueueFactory()
|
||||
// val taskQueue = taskQueueArray.getAndSet(position++, newQueue)
|
||||
// position %= wheelSize
|
||||
// while (true) {
|
||||
// val task = taskQueue.take() ?: break
|
||||
// if (task.canceled) {
|
||||
//
|
||||
// continue
|
||||
// } else if (task.isOutTime) {
|
||||
// outTimeQueue.offer(task)
|
||||
// } else {
|
||||
// newQueue.offer(task)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// runNow(outTimeQueue)
|
||||
//}
|
||||
thread(isDaemon = true, name = name) {
|
||||
val startTime = CurrentTimeMillisClock.now
|
||||
while (!closed) {
|
||||
position %= wheelSize
|
||||
|
||||
val newQueue = TaskQueue()
|
||||
val taskQueue = taskQueueArray[position]
|
||||
taskQueueArray[position] = newQueue
|
||||
|
||||
val time = System.currentTimeMillis()
|
||||
var node = taskQueue.root.next
|
||||
while (node != null) {
|
||||
node = if (node.isOutTime(time)) {
|
||||
val sNode = node
|
||||
threadPool.execute { sNode.task() }
|
||||
node.next
|
||||
|
||||
val outTimeQueue = taskQueueFactory()
|
||||
val newQueue = taskQueueFactory()
|
||||
val taskQueue = taskQueueArray.getAndSet(position++, newQueue)
|
||||
|
||||
while (true) {
|
||||
val node = taskQueue.take() ?: break
|
||||
if (node.canceled) {
|
||||
continue
|
||||
} else if (node.isOutTime) {
|
||||
outTimeQueue.offer(node)
|
||||
//runNow(node)
|
||||
} else {
|
||||
val next = node.next
|
||||
newQueue.offer(node)
|
||||
next
|
||||
}
|
||||
}
|
||||
|
||||
position++
|
||||
val nextSleep = startTime + tick * position - System.currentTimeMillis()
|
||||
|
||||
runNow(outTimeQueue)
|
||||
|
||||
val nextSleep = startTime + tick * position - CurrentTimeMillisClock.now
|
||||
if (nextSleep > 0) sleep(tick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TaskQueue {
|
||||
val root: TaskNode = TaskNode(0, {}, null, null)
|
||||
|
||||
fun offer(task: () -> Unit, timeout: Long): TaskNode {
|
||||
synchronized(root) {
|
||||
val insert = TaskNode(timeout, task, root, root.next)
|
||||
root.next = insert
|
||||
insert.next?.prev = insert
|
||||
return insert
|
||||
}
|
||||
}
|
||||
|
||||
fun offer(node: TaskNode): TaskNode {
|
||||
synchronized(root) {
|
||||
node.next = root.next
|
||||
node.next = node
|
||||
node.next?.prev = node
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
inner class TaskNode(
|
||||
val timeout: Long,
|
||||
val task: () -> Unit,
|
||||
var prev: TaskNode?,
|
||||
var next: TaskNode?
|
||||
) : TimerTask {
|
||||
val outTime = System.currentTimeMillis() + timeout
|
||||
val isOutTime get() = System.currentTimeMillis() > outTime
|
||||
|
||||
fun isOutTime(time: Long) = time > outTime
|
||||
|
||||
override fun run() = task()
|
||||
|
||||
override fun cancel() {
|
||||
synchronized(root) {
|
||||
prev?.next = next
|
||||
next?.prev = prev
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
val threadPool: ExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(),
|
||||
object : ThreadFactory {
|
||||
var threadNumber = 0
|
||||
override fun newThread(r: Runnable): Thread {
|
||||
val thread = Thread(r)
|
||||
thread.isDaemon = true
|
||||
thread.name = "wheelTimerWorker-$threadNumber"
|
||||
return thread
|
||||
}
|
||||
})
|
||||
val timer by lazy { WheelTimer(200, 1024) }
|
||||
val smoothTimer by lazy { WheelTimer(20, 128) }
|
||||
fun ScheduledThreadPoolExecutor.scheduleAtFixedRate(
|
||||
var2: Long,
|
||||
var4: Long,
|
||||
var6: TimeUnit,
|
||||
var1: () -> Unit
|
||||
): ScheduledFuture<*> = scheduleAtFixedRate(var1, var2, var4, var6)
|
||||
}
|
||||
}
|
@ -9,13 +9,13 @@ package cn.tursom.utils.bytebuffer
|
||||
* will support
|
||||
*/
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import cn.tursom.core.isStatic
|
||||
import cn.tursom.core.unsafe
|
||||
|
||||
class UnsupportedException : Exception()
|
||||
|
||||
fun AdvanceByteBuffer.serialize(obj: Any) {
|
||||
fun ByteBuffer.serialize(obj: Any) {
|
||||
when (obj) {
|
||||
is Char -> put(obj)
|
||||
is Short -> put(obj)
|
||||
@ -80,7 +80,7 @@ fun AdvanceByteBuffer.serialize(obj: Any) {
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> AdvanceByteBuffer.unSerialize(clazz: Class<T>): T {
|
||||
fun <T> ByteBuffer.unSerialize(clazz: Class<T>): T {
|
||||
when {
|
||||
clazz == Byte::class.java -> {
|
||||
return get() as T
|
||||
@ -188,6 +188,7 @@ fun <T> AdvanceByteBuffer.unSerialize(clazz: Class<T>): T {
|
||||
return newArray as T
|
||||
}
|
||||
else -> {
|
||||
//TODO
|
||||
val instance = try {
|
||||
clazz.newInstance()
|
||||
} catch (e: Throwable) {
|
||||
@ -203,6 +204,6 @@ fun <T> AdvanceByteBuffer.unSerialize(clazz: Class<T>): T {
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> AdvanceByteBuffer.unSerialize(): T {
|
||||
inline fun <reified T : Any> ByteBuffer.unSerialize(): T {
|
||||
return unSerialize(T::class.java)
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
package cn.tursom.utils.pool
|
||||
|
||||
import cn.tursom.core.datastruct.ArrayBitSet
|
||||
import cn.tursom.core.pool.AsyncPool
|
||||
import cn.tursom.utils.asynclock.AsyncLock
|
||||
import cn.tursom.utils.asynclock.AsyncMutexLock
|
||||
|
||||
class AsyncArrayPool<T>(private val lock: AsyncLock = AsyncMutexLock()) : AsyncPool<T> {
|
||||
private val bitSet = ArrayBitSet(64)
|
||||
private var poll = Array<Any?>(bitSet.size.toInt()) { null }
|
||||
|
||||
|
||||
override suspend fun put(cache: T): Boolean {
|
||||
val index = lock {
|
||||
val index = getDown()
|
||||
bitSet.up(index)
|
||||
index
|
||||
}.toInt()
|
||||
poll[index] = cache
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun get(): T? {
|
||||
return lock {
|
||||
val index = bitSet.firstUp()
|
||||
if (index < 0) null
|
||||
else {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val ret = poll[index.toInt()] as T
|
||||
bitSet.down(index)
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDown(): Long {
|
||||
val index = bitSet.firstDown()
|
||||
return if (index < 0) {
|
||||
resize()
|
||||
bitSet.firstDown()
|
||||
} else {
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
private fun resize() {
|
||||
bitSet.resize(bitSet.size * 2)
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package cn.tursom.utils.pool
|
||||
|
||||
import cn.tursom.core.pool.AsyncPool
|
||||
import cn.tursom.utils.asynclock.AsyncMutexLock
|
||||
|
||||
/**
|
||||
* 这个缓冲池不会释放自己持有的节点资源,防止频繁分配对象导致性能下降
|
||||
*/
|
||||
class AsyncCachedLinkedPool<T> : AsyncPool<T> {
|
||||
@Volatile
|
||||
private var rootNode: Node<T>? = null
|
||||
@Volatile
|
||||
private var cacheNode: Node<T>? = null
|
||||
private val lock = AsyncMutexLock()
|
||||
|
||||
override suspend fun put(cache: T): Boolean = lock {
|
||||
rootNode = if (cacheNode != null) {
|
||||
val node = cacheNode!!
|
||||
cacheNode = node.next
|
||||
node.next = rootNode
|
||||
rootNode = node
|
||||
node
|
||||
} else {
|
||||
Node(cache, rootNode)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
override suspend fun get(): T? = lock {
|
||||
val node = rootNode
|
||||
rootNode = rootNode?.next
|
||||
val value = node?.value
|
||||
node?.value = null
|
||||
node?.next = cacheNode
|
||||
cacheNode = node
|
||||
value
|
||||
}
|
||||
|
||||
private class Node<T>(var value: T? = null, var next: Node<T>? = null)
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package cn.tursom.utils.pool
|
||||
|
||||
import cn.tursom.core.pool.AsyncPool
|
||||
import cn.tursom.utils.asynclock.AsyncMutexLock
|
||||
|
||||
class AsyncLinkedCachePool<T> : AsyncPool<T> {
|
||||
@Volatile
|
||||
private var rootNode: Node<T>? = null
|
||||
private val lock = AsyncMutexLock()
|
||||
|
||||
override suspend fun put(cache: T): Boolean = lock {
|
||||
rootNode = Node(cache, rootNode)
|
||||
true
|
||||
}
|
||||
|
||||
override suspend fun get(): T? = lock {
|
||||
val node = rootNode
|
||||
rootNode = rootNode?.next
|
||||
node?.value
|
||||
}
|
||||
|
||||
private class Node<T>(var value: T? = null, var next: Node<T>? = null)
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package cn.tursom.utils.pool
|
||||
|
||||
import cn.tursom.core.bytebuffer.HeapByteBuffer
|
||||
import cn.tursom.core.datastruct.ArrayBitSet
|
||||
import cn.tursom.core.pool.AsyncPool
|
||||
import cn.tursom.utils.asynclock.AsyncMutexLock
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class AsyncMemoryPool(val blockSize: Int = 1024, val blockCount: Int = 16) : AsyncPool<ByteBuffer> {
|
||||
private val memoryPool = ByteArray(blockSize * blockCount)
|
||||
private val bitSet = ArrayBitSet(blockCount.toLong())
|
||||
private val lock = AsyncMutexLock()
|
||||
|
||||
override suspend fun put(cache: ByteBuffer): Boolean {
|
||||
if (cache.array() !== memoryPool) return false
|
||||
|
||||
val index = (cache.arrayOffset() / blockSize).toLong()
|
||||
lock {
|
||||
bitSet.down(index)
|
||||
}
|
||||
|
||||
cache.asCharBuffer()
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun get(): ByteBuffer? {
|
||||
val index = lock {
|
||||
val index = bitSet.firstDown()
|
||||
if (index >= 0) bitSet.up(index)
|
||||
index
|
||||
}.toInt()
|
||||
return if (index < 0 || index > blockCount) null
|
||||
else HeapByteBuffer.wrap(memoryPool, blockSize * index, blockSize)
|
||||
}
|
||||
|
||||
fun contain(buffer: ByteBuffer) = buffer.array() === memoryPool
|
||||
}
|
||||
|
||||
|
@ -1,146 +0,0 @@
|
||||
package cn.tursom.web.netty
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import io.netty.buffer.ByteBuf
|
||||
import java.io.OutputStream
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class NettyAdvanceByteBuffer(val byteBuf: ByteBuf) : AdvanceByteBuffer {
|
||||
override val hasArray: Boolean get() = byteBuf.hasArray()
|
||||
override val readOnly: Boolean get() = byteBuf.isReadOnly
|
||||
override val nioBuffer: ByteBuffer
|
||||
get() = if (readMode) byteBuf.nioBuffer()
|
||||
else byteBuf.nioBuffer(writePosition, limit)
|
||||
|
||||
override val bufferCount: Int get() = byteBuf.nioBufferCount()
|
||||
override val nioBuffers: Array<out ByteBuffer> get() = byteBuf.nioBuffers()
|
||||
|
||||
override var writePosition: Int
|
||||
get() = byteBuf.writerIndex()
|
||||
set(value) {
|
||||
byteBuf.writerIndex(value)
|
||||
}
|
||||
override var limit: Int
|
||||
get() = byteBuf.capacity()
|
||||
set(value) {
|
||||
byteBuf.capacity(value)
|
||||
}
|
||||
override val capacity get() = byteBuf.maxCapacity()
|
||||
override val array: ByteArray get() = byteBuf.array()
|
||||
override val arrayOffset: Int get() = byteBuf.arrayOffset()
|
||||
override var readPosition: Int
|
||||
get() = byteBuf.readerIndex()
|
||||
set(value) {
|
||||
byteBuf.readerIndex(value)
|
||||
}
|
||||
override val readOffset: Int get() = byteBuf.arrayOffset() + byteBuf.readerIndex()
|
||||
override val readableSize: Int
|
||||
get() = byteBuf.readableBytes()
|
||||
override val available: Int get() = readableSize
|
||||
override val writeOffset: Int get() = writePosition
|
||||
override val writeableSize: Int
|
||||
get() = limit - writePosition
|
||||
override val size: Int get() = capacity
|
||||
override var readMode: Boolean = false
|
||||
|
||||
override fun readMode() {
|
||||
readMode = true
|
||||
}
|
||||
|
||||
override fun resumeWriteMode(usedSize: Int) {
|
||||
readPosition += usedSize
|
||||
readMode = false
|
||||
}
|
||||
|
||||
override fun needReadSize(size: Int) {
|
||||
if (size > limit) {
|
||||
if (size < capacity) byteBuf.capacity(size)
|
||||
else throw IndexOutOfBoundsException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
byteBuf.clear()
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
byteBuf.discardReadBytes()
|
||||
}
|
||||
|
||||
override fun reset(outputStream: OutputStream) {
|
||||
byteBuf.readBytes(outputStream, readableSize)
|
||||
byteBuf.clear()
|
||||
}
|
||||
|
||||
override fun get(): Byte = byteBuf.readByte()
|
||||
override fun getChar(): Char = byteBuf.readChar()
|
||||
override fun getShort(): Short = byteBuf.readShort()
|
||||
override fun getInt(): Int = byteBuf.readInt()
|
||||
override fun getLong(): Long = byteBuf.readLong()
|
||||
override fun getFloat(): Float = byteBuf.readFloat()
|
||||
override fun getDouble(): Double = byteBuf.readDouble()
|
||||
|
||||
override fun getBytes(size: Int): ByteArray {
|
||||
val bytes = ByteArray(size)
|
||||
byteBuf.readBytes(bytes)
|
||||
return bytes
|
||||
}
|
||||
|
||||
override fun getString(size: Int): String {
|
||||
val str = byteBuf.toString(readPosition, size, Charsets.UTF_8)
|
||||
readPosition += size
|
||||
return str
|
||||
}
|
||||
|
||||
override fun writeTo(buffer: ByteArray, bufferOffset: Int, size: Int): Int {
|
||||
byteBuf.readBytes(buffer, bufferOffset, size)
|
||||
return size
|
||||
}
|
||||
|
||||
override fun writeTo(os: OutputStream): Int {
|
||||
val size = readableSize
|
||||
byteBuf.readBytes(os, size)
|
||||
reset()
|
||||
return size
|
||||
}
|
||||
|
||||
override fun put(byte: Byte) {
|
||||
byteBuf.writeByte(byte.toInt())
|
||||
}
|
||||
|
||||
override fun put(char: Char) {
|
||||
byteBuf.writeChar(char.toInt())
|
||||
}
|
||||
|
||||
override fun put(short: Short) {
|
||||
byteBuf.writeShort(short.toInt())
|
||||
}
|
||||
|
||||
override fun put(int: Int) {
|
||||
byteBuf.writeInt(int)
|
||||
}
|
||||
|
||||
override fun put(long: Long) {
|
||||
byteBuf.writeLong(long)
|
||||
}
|
||||
|
||||
override fun put(float: Float) {
|
||||
byteBuf.writeFloat(float)
|
||||
}
|
||||
|
||||
override fun put(double: Double) {
|
||||
byteBuf.writeDouble(double)
|
||||
}
|
||||
|
||||
override fun put(str: String) {
|
||||
byteBuf.writeCharSequence(str, Charsets.UTF_8)
|
||||
}
|
||||
|
||||
override fun put(byteArray: ByteArray, startIndex: Int, endIndex: Int) {
|
||||
byteBuf.writeBytes(byteArray, startIndex, endIndex - startIndex)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "NettyAdvanceByteBuffer(byteBuf=$byteBuf, readMode=$readMode)"
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package cn.tursom.web.netty
|
||||
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import io.netty.buffer.ByteBuf
|
||||
import java.io.OutputStream
|
||||
|
||||
class NettyByteBuffer(val byteBuf: ByteBuf) : ByteBuffer {
|
||||
override val hasArray: Boolean get() = byteBuf.hasArray()
|
||||
override var writePosition: Int
|
||||
get() = byteBuf.writerIndex()
|
||||
set(value) {
|
||||
byteBuf.writerIndex(value)
|
||||
}
|
||||
override val capacity get() = byteBuf.maxCapacity()
|
||||
override val array: ByteArray get() = byteBuf.array()
|
||||
override val arrayOffset: Int get() = byteBuf.arrayOffset()
|
||||
override var readPosition: Int
|
||||
get() = byteBuf.readerIndex()
|
||||
set(value) {
|
||||
byteBuf.readerIndex(value)
|
||||
}
|
||||
override val resized: Boolean get() = false
|
||||
|
||||
override fun readBuffer(): java.nio.ByteBuffer {
|
||||
return byteBuf.internalNioBuffer(readPosition, readable)
|
||||
}
|
||||
|
||||
override fun writeBuffer(): java.nio.ByteBuffer {
|
||||
return byteBuf.internalNioBuffer(writePosition, writeable)
|
||||
}
|
||||
|
||||
override val readOffset: Int get() = byteBuf.arrayOffset() + byteBuf.readerIndex()
|
||||
|
||||
override fun clear() {
|
||||
byteBuf.clear()
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
byteBuf.discardReadBytes()
|
||||
}
|
||||
|
||||
override fun slice(offset: Int, size: Int): ByteBuffer {
|
||||
return NettyByteBuffer(byteBuf.slice(offset, size))
|
||||
}
|
||||
|
||||
override fun resize(newSize: Int): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun get(): Byte = byteBuf.readByte()
|
||||
override fun getChar(): Char = byteBuf.readChar()
|
||||
override fun getShort(): Short = byteBuf.readShort()
|
||||
override fun getInt(): Int = byteBuf.readInt()
|
||||
override fun getLong(): Long = byteBuf.readLong()
|
||||
override fun getFloat(): Float = byteBuf.readFloat()
|
||||
override fun getDouble(): Double = byteBuf.readDouble()
|
||||
|
||||
override fun getBytes(size: Int): ByteArray {
|
||||
val bytes = ByteArray(size)
|
||||
byteBuf.readBytes(bytes)
|
||||
return bytes
|
||||
}
|
||||
|
||||
override fun getString(size: Int): String {
|
||||
val str = byteBuf.toString(readPosition, size, Charsets.UTF_8)
|
||||
readPosition += size
|
||||
return str
|
||||
}
|
||||
|
||||
override fun writeTo(buffer: ByteArray, bufferOffset: Int, size: Int): Int {
|
||||
byteBuf.readBytes(buffer, bufferOffset, size)
|
||||
return size
|
||||
}
|
||||
|
||||
override fun writeTo(os: OutputStream): Int {
|
||||
val size = readable
|
||||
byteBuf.readBytes(os, size)
|
||||
reset()
|
||||
return size
|
||||
}
|
||||
|
||||
override fun put(byte: Byte) {
|
||||
byteBuf.writeByte(byte.toInt())
|
||||
}
|
||||
|
||||
override fun put(char: Char) {
|
||||
byteBuf.writeChar(char.toInt())
|
||||
}
|
||||
|
||||
override fun put(short: Short) {
|
||||
byteBuf.writeShort(short.toInt())
|
||||
}
|
||||
|
||||
override fun put(int: Int) {
|
||||
byteBuf.writeInt(int)
|
||||
}
|
||||
|
||||
override fun put(long: Long) {
|
||||
byteBuf.writeLong(long)
|
||||
}
|
||||
|
||||
override fun put(float: Float) {
|
||||
byteBuf.writeFloat(float)
|
||||
}
|
||||
|
||||
override fun put(double: Double) {
|
||||
byteBuf.writeDouble(double)
|
||||
}
|
||||
|
||||
override fun put(str: String) {
|
||||
byteBuf.writeCharSequence(str, Charsets.UTF_8)
|
||||
}
|
||||
|
||||
override fun put(byteArray: ByteArray, startIndex: Int, endIndex: Int) {
|
||||
byteBuf.writeBytes(byteArray, startIndex, endIndex - startIndex)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Nettyjava.nio.ByteBuffer(byteBuf=$byteBuf)"
|
||||
}
|
||||
}
|
@ -1,39 +1,42 @@
|
||||
package cn.tursom.web.netty
|
||||
|
||||
import cn.tursom.core.bytebuffer.AdvanceByteBuffer
|
||||
import cn.tursom.core.buffer.ByteBuffer
|
||||
import io.netty.buffer.ByteBuf
|
||||
import io.netty.buffer.ByteBufAllocator
|
||||
import io.netty.buffer.Unpooled
|
||||
import io.netty.channel.ChannelHandlerContext
|
||||
import io.netty.handler.stream.ChunkedInput
|
||||
|
||||
class NettyChunkedByteBuffer(val bufList: List<AdvanceByteBuffer>) : ChunkedInput<ByteBuf> {
|
||||
constructor(vararg bufList: AdvanceByteBuffer) : this(bufList.asList())
|
||||
class NettyChunkedByteBuffer(private val bufList: List<ByteBuffer>) : ChunkedInput<ByteBuf> {
|
||||
constructor(vararg bufList: ByteBuffer) : this(bufList.asList())
|
||||
|
||||
var iterator = bufList.iterator()
|
||||
var progress: Long = 0
|
||||
val length = run {
|
||||
var len = 0L
|
||||
bufList.forEach {
|
||||
len += it.readableSize
|
||||
}
|
||||
len
|
||||
}
|
||||
private var next: ByteBuffer? = null
|
||||
val iterator = bufList.iterator()
|
||||
private var progress: Long = 0
|
||||
private val length = run {
|
||||
var len = 0L
|
||||
bufList.forEach {
|
||||
len += it.readable
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
override fun progress(): Long = progress
|
||||
override fun length(): Long = length
|
||||
override fun isEndOfInput(): Boolean = !iterator.hasNext()
|
||||
override fun progress(): Long = progress
|
||||
override fun length(): Long = length
|
||||
override fun isEndOfInput(): Boolean = !iterator.hasNext()
|
||||
|
||||
override fun readChunk(ctx: ChannelHandlerContext?): ByteBuf = readChunk()
|
||||
override fun readChunk(allocator: ByteBufAllocator?): ByteBuf = readChunk()
|
||||
override fun readChunk(ctx: ChannelHandlerContext?): ByteBuf = readChunk()
|
||||
override fun readChunk(allocator: ByteBufAllocator?): ByteBuf = readChunk()
|
||||
|
||||
private fun readChunk(): ByteBuf {
|
||||
val next = iterator.next()
|
||||
progress += next.readableSize
|
||||
return if (next is NettyAdvanceByteBuffer) next.byteBuf
|
||||
else Unpooled.wrappedBuffer(next.nioBuffer)
|
||||
}
|
||||
private fun readChunk(): ByteBuf {
|
||||
this.next?.close()
|
||||
val next = iterator.next()
|
||||
this.next = next
|
||||
progress += next.readable
|
||||
return if (next is NettyByteBuffer) next.byteBuf
|
||||
else Unpooled.wrappedBuffer(next.readBuffer())
|
||||
}
|
||||
|
||||
override fun close() {}
|
||||
override fun close() {}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user