From 75483b45a1c15225627e9ca33a384ffdc1d31b70 Mon Sep 17 00:00:00 2001 From: tursom <tursom@foxmail.com> Date: Wed, 20 Nov 2019 13:09:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86AdvanceByteBuffer=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E4=B8=BA=E6=9B=B4=E5=8A=A0=E7=AE=80=E6=B4=81=E7=9A=84ByteBuffe?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/cn/tursom/niothread/NioThread.kt | 55 +++ .../tursom/niothread/NioThreadTaskFuture.kt | 5 + .../tursom/niothread/ThreadPoolNioThread.kt | 78 +++ .../tursom/niothread/WorkerLoopNioThread.kt | 113 +++++ .../niothread/loophandler/BossLoopHandler.kt | 35 ++ .../loophandler/WorkerLoopHandler.kt | 29 ++ .../kotlin/cn/tursom/socket/AsyncSocket.kt | 87 ++++ .../cn/tursom/socket/BufferedAsyncSocket.kt | 9 + .../cn/tursom/socket/BufferedNioSocket.kt | 8 + .../main/kotlin/cn/tursom/socket/NioClient.kt | 51 ++ .../kotlin/cn/tursom/socket/NioProtocol.kt | 23 + .../main/kotlin/cn/tursom/socket/NioSocket.kt | 163 +++++++ .../tursom/socket/server/BuffedNioServer.kt | 46 ++ .../cn/tursom/socket/server/NioLoopServer.kt | 58 +++ .../cn/tursom/socket/server/NioServer.kt | 38 ++ .../cn/tursom/socket/server/SocketServer.kt | 14 + settings.gradle | 2 + .../kotlin/cn/tursom/socket/AsyncNioSocket.kt | 2 - .../kotlin/cn/tursom/socket/AsyncSocket.kt | 55 +-- .../cn/tursom/socket/IAsyncNioSocket.kt | 34 +- .../cn/tursom/socket/enhance/SocketReader.kt | 6 +- .../cn/tursom/socket/enhance/SocketWriter.kt | 8 +- .../impl/LengthFieldBasedFrameReader.kt | 24 +- .../enhance/impl/LengthFieldPrependWriter.kt | 45 +- .../socket/enhance/impl/SimpSocketReader.kt | 6 +- .../socket/enhance/impl/SimpSocketWriter.kt | 27 +- .../socket/enhance/impl/StringReader.kt | 6 +- .../socket/enhance/impl/StringSocket.kt | 14 +- .../socket/enhance/impl/StringWriter.kt | 28 +- .../socket/enhance/impl/TimeoutReader.kt | 6 +- .../socket/server/BuffedAsyncNioServer.kt | 16 +- socket/socket-async/src/test/kotlin/Demo.kt | 30 -- .../src/test/kotlin/ProcessorTest.kt | 93 ---- .../socket/server/AsyncNioServerTest.kt | 59 --- .../socket/niothread/WorkerLoopNioThread.kt | 2 - .../cn/tursom/socket/server/NioServerTest.kt | 66 --- src/main/kotlin/cn/tursom/core/AsyncFile.kt | 135 +++--- .../kotlin/cn/tursom/core/AtomicBitSet.kt | 186 ++++++++ .../cn/tursom/core/CurrentTimeMillisClock.kt | 22 + src/main/kotlin/cn/tursom/core/DataOperate.kt | 4 +- .../cn/tursom/core/HeapByteBufferUtil.kt | 26 + src/main/kotlin/cn/tursom/core/LongBitSet.kt | 95 ++++ .../cn/tursom/core/NonLockLinkedList.kt | 31 ++ .../cn/tursom/core/buffer/ByteBuffer.kt | 228 +++++++++ .../tursom/core/buffer/ByteBufferExtension.kt | 79 ++++ .../tursom/core/buffer/MarkableByteBuffer.kt | 8 + .../tursom/core/buffer/MultipleByteBuffer.kt | 51 ++ .../cn/tursom/core/buffer/ProxyByteBuffer.kt | 7 + .../core/buffer/impl/ArrayByteBuffer.kt | 8 + .../core/buffer/impl/ArrayListByteBuffer.kt | 8 + .../core/buffer/impl/DirectByteBuffer.kt | 69 +++ .../tursom/core/buffer/impl/HeapByteBuffer.kt | 76 +++ .../core/buffer/impl/InstantByteBuffer.kt | 20 + .../tursom/core/buffer/impl/ListByteBuffer.kt | 6 + .../core/buffer/impl/MarkedByteBuffer.kt | 20 + .../core/buffer/impl/PooledByteBuffer.kt | 52 ++ .../core/buffer/impl/SplitByteBuffer.kt | 46 ++ .../core/bytebuffer/AdvanceByteBuffer.kt | 445 ------------------ .../bytebuffer/ByteArrayAdvanceByteBuffer.kt | 161 ------- .../bytebuffer/DirectNioAdvanceByteBuffer.kt | 68 --- .../tursom/core/bytebuffer/HeapByteBuffer.kt | 26 - .../bytebuffer/HeapNioAdvanceByteBuffer.kt | 176 ------- .../core/bytebuffer/MultiAdvanceByteBuffer.kt | 108 ----- .../core/bytebuffer/NioAdvanceByteBuffer.kt | 10 - .../core/bytebuffer/OutOfBufferException.kt | 6 - .../cn/tursom/core/pool/AbstractMemoryPool.kt | 62 +++ .../kotlin/cn/tursom/core/pool/ArrayPool.kt | 43 -- .../kotlin/cn/tursom/core/pool/AsyncPool.kt | 10 - .../cn/tursom/core/pool/CachedLinkedPool.kt | 37 -- .../cn/tursom/core/pool/DirectMemoryPool.kt | 61 +-- .../tursom/core/pool/ExpandableMemoryPool.kt | 69 +++ .../cn/tursom/core/pool/HeapMemoryPool.kt | 66 +-- .../cn/tursom/core/pool/InstantMemoryPool.kt | 34 ++ .../kotlin/cn/tursom/core/pool/LinkedPool.kt | 38 +- .../core/pool/LongBitSetAbstractMemoryPool.kt | 69 +++ .../core/pool/LongBitSetDirectMemoryPool.kt | 13 + .../core/pool/LongBitSetHeapMemoryPool.kt | 13 + .../cn/tursom/core/pool/MarkedMemoryPool.kt | 47 ++ .../kotlin/cn/tursom/core/pool/MemoryPool.kt | 59 +-- src/main/kotlin/cn/tursom/core/pool/Pool.kt | 18 +- .../tursom/core/pool/ScalabilityMemoryPool.kt | 76 +++ .../tursom/core/pool/ThreadLocalMemoryPool.kt | 30 ++ .../pool/ThreadUnsafeAbstractMemoryPool.kt | 68 +++ .../core/pool/ThreadUnsafeDirectMemoryPool.kt | 21 + .../core/pool/ThreadUnsafeHeapMemoryPool.kt | 21 + .../cn/tursom/core/timer/NonLockTaskQueue.kt | 55 +++ .../cn/tursom/core/timer/StaticWheelTimer.kt | 102 ---- .../core/timer/SynchronizedTaskQueue.kt | 69 +++ .../kotlin/cn/tursom/core/timer/TaskQueue.kt | 7 + src/main/kotlin/cn/tursom/core/timer/Timer.kt | 38 +- .../kotlin/cn/tursom/core/timer/TimerTask.kt | 15 +- .../kotlin/cn/tursom/core/timer/WheelTimer.kt | 145 +++--- .../cn/tursom/utils/bytebuffer/serialize.kt | 9 +- .../cn/tursom/utils/pool/AsyncArrayPool.kt | 49 -- .../utils/pool/AsyncCachedLinkedPool.kt | 40 -- .../tursom/utils/pool/AsyncLinkedCachePool.kt | 23 - .../cn/tursom/utils/pool/AsyncMemoryPool.kt | 40 -- .../web/netty/NettyAdvanceByteBuffer.kt | 146 ------ .../cn/tursom/web/netty/NettyByteBuffer.kt | 121 +++++ .../web/netty/NettyChunkedByteBuffer.kt | 51 +- .../cn/tursom/web/netty/NettyChunkedInput.kt | 8 +- .../tursom/web/netty/NettyExceptionContent.kt | 6 +- .../cn/tursom/web/netty/NettyHttpContent.kt | 14 +- .../kotlin/cn/tursom/web/ExceptionContent.kt | 5 +- .../main/kotlin/cn/tursom/web/HttpContent.kt | 278 +++++------ .../kotlin/cn/tursom/web/utils/Chunked.kt | 4 +- .../cn/tursom/web/utils/EmptyHttpContent.kt | 66 +-- 107 files changed, 3245 insertions(+), 2419 deletions(-) create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThread.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThreadTaskFuture.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/niothread/ThreadPoolNioThread.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/niothread/WorkerLoopNioThread.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/niothread/loophandler/BossLoopHandler.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/niothread/loophandler/WorkerLoopHandler.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/BufferedAsyncSocket.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/BufferedNioSocket.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/NioClient.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/NioProtocol.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/NioSocket.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/server/BuffedNioServer.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/server/NioLoopServer.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/server/NioServer.kt create mode 100644 AsyncSocket/src/main/kotlin/cn/tursom/socket/server/SocketServer.kt delete mode 100644 socket/socket-async/src/test/kotlin/Demo.kt delete mode 100644 socket/socket-async/src/test/kotlin/ProcessorTest.kt delete mode 100644 socket/socket-async/src/test/kotlin/cn/tursom/socket/server/AsyncNioServerTest.kt delete mode 100644 socket/src/test/kotlin/cn/tursom/socket/server/NioServerTest.kt create mode 100644 src/main/kotlin/cn/tursom/core/AtomicBitSet.kt create mode 100644 src/main/kotlin/cn/tursom/core/CurrentTimeMillisClock.kt create mode 100644 src/main/kotlin/cn/tursom/core/HeapByteBufferUtil.kt create mode 100644 src/main/kotlin/cn/tursom/core/LongBitSet.kt create mode 100644 src/main/kotlin/cn/tursom/core/NonLockLinkedList.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/ByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/ByteBufferExtension.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/MarkableByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/MultipleByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/ProxyByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/impl/ArrayByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/impl/ArrayListByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/impl/DirectByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/impl/HeapByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/impl/InstantByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/impl/ListByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/impl/MarkedByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/impl/PooledByteBuffer.kt create mode 100644 src/main/kotlin/cn/tursom/core/buffer/impl/SplitByteBuffer.kt delete mode 100644 src/main/kotlin/cn/tursom/core/bytebuffer/AdvanceByteBuffer.kt delete mode 100644 src/main/kotlin/cn/tursom/core/bytebuffer/ByteArrayAdvanceByteBuffer.kt delete mode 100644 src/main/kotlin/cn/tursom/core/bytebuffer/DirectNioAdvanceByteBuffer.kt delete mode 100644 src/main/kotlin/cn/tursom/core/bytebuffer/HeapByteBuffer.kt delete mode 100644 src/main/kotlin/cn/tursom/core/bytebuffer/HeapNioAdvanceByteBuffer.kt delete mode 100644 src/main/kotlin/cn/tursom/core/bytebuffer/MultiAdvanceByteBuffer.kt delete mode 100644 src/main/kotlin/cn/tursom/core/bytebuffer/NioAdvanceByteBuffer.kt delete mode 100644 src/main/kotlin/cn/tursom/core/bytebuffer/OutOfBufferException.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/AbstractMemoryPool.kt delete mode 100644 src/main/kotlin/cn/tursom/core/pool/ArrayPool.kt delete mode 100644 src/main/kotlin/cn/tursom/core/pool/AsyncPool.kt delete mode 100644 src/main/kotlin/cn/tursom/core/pool/CachedLinkedPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/ExpandableMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/InstantMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/LongBitSetAbstractMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/LongBitSetDirectMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/LongBitSetHeapMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/MarkedMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/ScalabilityMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/ThreadLocalMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeAbstractMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeDirectMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeHeapMemoryPool.kt create mode 100644 src/main/kotlin/cn/tursom/core/timer/NonLockTaskQueue.kt delete mode 100644 src/main/kotlin/cn/tursom/core/timer/StaticWheelTimer.kt create mode 100644 src/main/kotlin/cn/tursom/core/timer/SynchronizedTaskQueue.kt create mode 100644 src/main/kotlin/cn/tursom/core/timer/TaskQueue.kt delete mode 100644 utils/src/main/kotlin/cn/tursom/utils/pool/AsyncArrayPool.kt delete mode 100644 utils/src/main/kotlin/cn/tursom/utils/pool/AsyncCachedLinkedPool.kt delete mode 100644 utils/src/main/kotlin/cn/tursom/utils/pool/AsyncLinkedCachePool.kt delete mode 100644 utils/src/main/kotlin/cn/tursom/utils/pool/AsyncMemoryPool.kt delete mode 100644 web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyAdvanceByteBuffer.kt create mode 100644 web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyByteBuffer.kt diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThread.kt b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThread.kt new file mode 100644 index 0000000..ba0ca34 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThread.kt @@ -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> +} + diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThreadTaskFuture.kt b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThreadTaskFuture.kt new file mode 100644 index 0000000..c13f109 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/NioThreadTaskFuture.kt @@ -0,0 +1,5 @@ +package cn.tursom.niothread + +interface NioThreadTaskFuture<T> { + fun get(): T +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/niothread/ThreadPoolNioThread.kt b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/ThreadPoolNioThread.kt new file mode 100644 index 0000000..c729c7d --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/ThreadPoolNioThread.kt @@ -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)" + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/niothread/WorkerLoopNioThread.kt b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/WorkerLoopNioThread.kt new file mode 100644 index 0000000..db46dc4 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/WorkerLoopNioThread.kt @@ -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() + } + } + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/niothread/loophandler/BossLoopHandler.kt b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/loophandler/BossLoopHandler.kt new file mode 100644 index 0000000..b8c37bd --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/loophandler/BossLoopHandler.kt @@ -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() + } + } + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/niothread/loophandler/WorkerLoopHandler.kt b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/loophandler/WorkerLoopHandler.kt new file mode 100644 index 0000000..27993e6 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/niothread/loophandler/WorkerLoopHandler.kt @@ -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() + } + } + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt new file mode 100644 index 0000000..285fe76 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt @@ -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 + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/BufferedAsyncSocket.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/BufferedAsyncSocket.kt new file mode 100644 index 0000000..3b5365a --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/BufferedAsyncSocket.kt @@ -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) +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/BufferedNioSocket.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/BufferedNioSocket.kt new file mode 100644 index 0000000..dbb7190 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/BufferedNioSocket.kt @@ -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 \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/NioClient.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/NioClient.kt new file mode 100644 index 0000000..60e36d3 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/NioClient.kt @@ -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 + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/NioProtocol.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/NioProtocol.kt new file mode 100644 index 0000000..d2a060a --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/NioProtocol.kt @@ -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() + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/NioSocket.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/NioSocket.kt new file mode 100644 index 0000000..38180c6 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/NioSocket.kt @@ -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 + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/BuffedNioServer.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/BuffedNioServer.kt new file mode 100644 index 0000000..45063c2 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/BuffedNioServer.kt @@ -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() + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/NioLoopServer.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/NioLoopServer.kt new file mode 100644 index 0000000..4aa2cf9 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/NioLoopServer.kt @@ -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 + } +} \ No newline at end of file diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/NioServer.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/NioServer.kt new file mode 100644 index 0000000..1b7ca66 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/NioServer.kt @@ -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) + diff --git a/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/SocketServer.kt b/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/SocketServer.kt new file mode 100644 index 0000000..09c25a0 --- /dev/null +++ b/AsyncSocket/src/main/kotlin/cn/tursom/socket/server/SocketServer.kt @@ -0,0 +1,14 @@ +package cn.tursom.socket.server + +import java.io.Closeable + +/** + * 套接字服务器的基本形式,提供运行、关闭的基本操作 + * 其应支持最基本的创建形式: + * XXXServer(port) { + * // 业务逻辑 + * } + */ +interface SocketServer : Runnable, Closeable { + val port: Int +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 56524e8..df2ce7a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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' + diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/AsyncNioSocket.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/AsyncNioSocket.kt index 12f73b1..0c420b8 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/AsyncNioSocket.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/AsyncNioSocket.kt @@ -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 diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt index 407683b..ca0b33e 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/AsyncSocket.kt @@ -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 + } } \ No newline at end of file diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/IAsyncNioSocket.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/IAsyncNioSocket.kt index 53c13c4..daef883 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/IAsyncNioSocket.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/IAsyncNioSocket.kt @@ -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 diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/SocketReader.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/SocketReader.kt index 875e2b6..2deb6b7 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/SocketReader.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/SocketReader.kt @@ -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() } diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/SocketWriter.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/SocketWriter.kt index 32f068c..cbfa1a1 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/SocketWriter.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/SocketWriter.kt @@ -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) } \ No newline at end of file diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/LengthFieldBasedFrameReader.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/LengthFieldBasedFrameReader.kt index 443b18c..f409734 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/LengthFieldBasedFrameReader.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/LengthFieldBasedFrameReader.kt @@ -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 diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/LengthFieldPrependWriter.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/LengthFieldPrependWriter.kt index dc73c52..d4f2daa 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/LengthFieldPrependWriter.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/LengthFieldPrependWriter.kt @@ -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) } + } } diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/SimpSocketReader.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/SimpSocketReader.kt index eba6a06..31eadae 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/SimpSocketReader.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/SimpSocketReader.kt @@ -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() diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/SimpSocketWriter.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/SimpSocketWriter.kt index ac78cc2..99b16e4 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/SimpSocketWriter.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/SimpSocketWriter.kt @@ -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() + } } \ No newline at end of file diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringReader.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringReader.kt index 889887a..8dccb98 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringReader.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringReader.kt @@ -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() } diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringSocket.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringSocket.kt index 3dc5eec..1911590 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringSocket.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringSocket.kt @@ -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) ) \ No newline at end of file diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringWriter.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringWriter.kt index 96fb74f..5a0bb2d 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringWriter.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/StringWriter.kt @@ -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() + } } \ No newline at end of file diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/TimeoutReader.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/TimeoutReader.kt index 3c1b831..b20722d 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/TimeoutReader.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/enhance/impl/TimeoutReader.kt @@ -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() diff --git a/socket/socket-async/src/main/kotlin/cn/tursom/socket/server/BuffedAsyncNioServer.kt b/socket/socket-async/src/main/kotlin/cn/tursom/socket/server/BuffedAsyncNioServer.kt index 6c57b2f..140f9dc 100644 --- a/socket/socket-async/src/main/kotlin/cn/tursom/socket/server/BuffedAsyncNioServer.kt +++ b/socket/socket-async/src/main/kotlin/cn/tursom/socket/server/BuffedAsyncNioServer.kt @@ -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) } \ No newline at end of file diff --git a/socket/socket-async/src/test/kotlin/Demo.kt b/socket/socket-async/src/test/kotlin/Demo.kt deleted file mode 100644 index 3926d1a..0000000 --- a/socket/socket-async/src/test/kotlin/Demo.kt +++ /dev/null @@ -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() -} \ No newline at end of file diff --git a/socket/socket-async/src/test/kotlin/ProcessorTest.kt b/socket/socket-async/src/test/kotlin/ProcessorTest.kt deleted file mode 100644 index af76a01..0000000 --- a/socket/socket-async/src/test/kotlin/ProcessorTest.kt +++ /dev/null @@ -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() -} \ No newline at end of file diff --git a/socket/socket-async/src/test/kotlin/cn/tursom/socket/server/AsyncNioServerTest.kt b/socket/socket-async/src/test/kotlin/cn/tursom/socket/server/AsyncNioServerTest.kt deleted file mode 100644 index f22ce42..0000000 --- a/socket/socket-async/src/test/kotlin/cn/tursom/socket/server/AsyncNioServerTest.kt +++ /dev/null @@ -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) - } - } - } -} \ No newline at end of file diff --git a/socket/src/main/kotlin/cn/tursom/socket/niothread/WorkerLoopNioThread.kt b/socket/src/main/kotlin/cn/tursom/socket/niothread/WorkerLoopNioThread.kt index 6f5585f..06ea4ec 100644 --- a/socket/src/main/kotlin/cn/tursom/socket/niothread/WorkerLoopNioThread.kt +++ b/socket/src/main/kotlin/cn/tursom/socket/niothread/WorkerLoopNioThread.kt @@ -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( diff --git a/socket/src/test/kotlin/cn/tursom/socket/server/NioServerTest.kt b/socket/src/test/kotlin/cn/tursom/socket/server/NioServerTest.kt deleted file mode 100644 index 8ee99e2..0000000 --- a/socket/src/test/kotlin/cn/tursom/socket/server/NioServerTest.kt +++ /dev/null @@ -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() - } -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/AsyncFile.kt b/src/main/kotlin/cn/tursom/core/AsyncFile.kt index 066fc05..fd4f0c2 100644 --- a/src/main/kotlin/cn/tursom/core/AsyncFile.kt +++ b/src/main/kotlin/cn/tursom/core/AsyncFile.kt @@ -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) + } + } } \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/AtomicBitSet.kt b/src/main/kotlin/cn/tursom/core/AtomicBitSet.kt new file mode 100644 index 0000000..a5f1465 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/AtomicBitSet.kt @@ -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)) + } + } + } +} diff --git a/src/main/kotlin/cn/tursom/core/CurrentTimeMillisClock.kt b/src/main/kotlin/cn/tursom/core/CurrentTimeMillisClock.kt new file mode 100644 index 0000000..0731255 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/CurrentTimeMillisClock.kt @@ -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() +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/DataOperate.kt b/src/main/kotlin/cn/tursom/core/DataOperate.kt index 1c3d856..d25e943 100644 --- a/src/main/kotlin/cn/tursom/core/DataOperate.kt +++ b/src/main/kotlin/cn/tursom/core/DataOperate.kt @@ -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) { diff --git a/src/main/kotlin/cn/tursom/core/HeapByteBufferUtil.kt b/src/main/kotlin/cn/tursom/core/HeapByteBufferUtil.kt new file mode 100644 index 0000000..34d15ee --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/HeapByteBufferUtil.kt @@ -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()) +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/LongBitSet.kt b/src/main/kotlin/cn/tursom/core/LongBitSet.kt new file mode 100644 index 0000000..f486d87 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/LongBitSet.kt @@ -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)] + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/NonLockLinkedList.kt b/src/main/kotlin/cn/tursom/core/NonLockLinkedList.kt new file mode 100644 index 0000000..bfbc572 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/NonLockLinkedList.kt @@ -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() +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/ByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/ByteBuffer.kt new file mode 100644 index 0000000..dd78899 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/ByteBuffer.kt @@ -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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/ByteBufferExtension.kt b/src/main/kotlin/cn/tursom/core/buffer/ByteBufferExtension.kt new file mode 100644 index 0000000..c4a252d --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/ByteBufferExtension.kt @@ -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) \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/MarkableByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/MarkableByteBuffer.kt new file mode 100644 index 0000000..809a9ed --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/MarkableByteBuffer.kt @@ -0,0 +1,8 @@ +package cn.tursom.buffer + +import cn.tursom.core.buffer.ByteBuffer + +interface MarkableByteBuffer : ByteBuffer { + fun mark() + fun resume() +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/MultipleByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/MultipleByteBuffer.kt new file mode 100644 index 0000000..2710a25 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/MultipleByteBuffer.kt @@ -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) +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/ProxyByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/ProxyByteBuffer.kt new file mode 100644 index 0000000..54fa742 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/ProxyByteBuffer.kt @@ -0,0 +1,7 @@ +package cn.tursom.buffer + +import cn.tursom.core.buffer.ByteBuffer + +interface ProxyByteBuffer : ByteBuffer { + val agent: ByteBuffer +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/impl/ArrayByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/impl/ArrayByteBuffer.kt new file mode 100644 index 0000000..7397077 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/impl/ArrayByteBuffer.kt @@ -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() \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/impl/ArrayListByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/impl/ArrayListByteBuffer.kt new file mode 100644 index 0000000..ceeecb8 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/impl/ArrayListByteBuffer.kt @@ -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() +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/impl/DirectByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/impl/DirectByteBuffer.kt new file mode 100644 index 0000000..2602619 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/impl/DirectByteBuffer.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/impl/HeapByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/impl/HeapByteBuffer.kt new file mode 100644 index 0000000..64c47da --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/impl/HeapByteBuffer.kt @@ -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) + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/impl/InstantByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/impl/InstantByteBuffer.kt new file mode 100644 index 0000000..45ad3f9 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/impl/InstantByteBuffer.kt @@ -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)" +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/impl/ListByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/impl/ListByteBuffer.kt new file mode 100644 index 0000000..cf28407 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/impl/ListByteBuffer.kt @@ -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 \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/impl/MarkedByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/impl/MarkedByteBuffer.kt new file mode 100644 index 0000000..5c3a46c --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/impl/MarkedByteBuffer.kt @@ -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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/impl/PooledByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/impl/PooledByteBuffer.kt new file mode 100644 index 0000000..f511d64 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/impl/PooledByteBuffer.kt @@ -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) + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/buffer/impl/SplitByteBuffer.kt b/src/main/kotlin/cn/tursom/core/buffer/impl/SplitByteBuffer.kt new file mode 100644 index 0000000..00f592c --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/buffer/impl/SplitByteBuffer.kt @@ -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() + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/bytebuffer/AdvanceByteBuffer.kt b/src/main/kotlin/cn/tursom/core/bytebuffer/AdvanceByteBuffer.kt deleted file mode 100644 index b5c01d0..0000000 --- a/src/main/kotlin/cn/tursom/core/bytebuffer/AdvanceByteBuffer.kt +++ /dev/null @@ -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() - } - } -} - diff --git a/src/main/kotlin/cn/tursom/core/bytebuffer/ByteArrayAdvanceByteBuffer.kt b/src/main/kotlin/cn/tursom/core/bytebuffer/ByteArrayAdvanceByteBuffer.kt deleted file mode 100644 index 9a39462..0000000 --- a/src/main/kotlin/cn/tursom/core/bytebuffer/ByteArrayAdvanceByteBuffer.kt +++ /dev/null @@ -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() -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/bytebuffer/DirectNioAdvanceByteBuffer.kt b/src/main/kotlin/cn/tursom/core/bytebuffer/DirectNioAdvanceByteBuffer.kt deleted file mode 100644 index cd4cb66..0000000 --- a/src/main/kotlin/cn/tursom/core/bytebuffer/DirectNioAdvanceByteBuffer.kt +++ /dev/null @@ -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)" - } -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/bytebuffer/HeapByteBuffer.kt b/src/main/kotlin/cn/tursom/core/bytebuffer/HeapByteBuffer.kt deleted file mode 100644 index 0af97f7..0000000 --- a/src/main/kotlin/cn/tursom/core/bytebuffer/HeapByteBuffer.kt +++ /dev/null @@ -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()) -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/bytebuffer/HeapNioAdvanceByteBuffer.kt b/src/main/kotlin/cn/tursom/core/bytebuffer/HeapNioAdvanceByteBuffer.kt deleted file mode 100644 index 3df55d4..0000000 --- a/src/main/kotlin/cn/tursom/core/bytebuffer/HeapNioAdvanceByteBuffer.kt +++ /dev/null @@ -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)" - } -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/bytebuffer/MultiAdvanceByteBuffer.kt b/src/main/kotlin/cn/tursom/core/bytebuffer/MultiAdvanceByteBuffer.kt deleted file mode 100644 index 5d68b54..0000000 --- a/src/main/kotlin/cn/tursom/core/bytebuffer/MultiAdvanceByteBuffer.kt +++ /dev/null @@ -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)" - } - - -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/bytebuffer/NioAdvanceByteBuffer.kt b/src/main/kotlin/cn/tursom/core/bytebuffer/NioAdvanceByteBuffer.kt deleted file mode 100644 index 7ddb0c0..0000000 --- a/src/main/kotlin/cn/tursom/core/bytebuffer/NioAdvanceByteBuffer.kt +++ /dev/null @@ -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) - } diff --git a/src/main/kotlin/cn/tursom/core/bytebuffer/OutOfBufferException.kt b/src/main/kotlin/cn/tursom/core/bytebuffer/OutOfBufferException.kt deleted file mode 100644 index dc58315..0000000 --- a/src/main/kotlin/cn/tursom/core/bytebuffer/OutOfBufferException.kt +++ /dev/null @@ -1,6 +0,0 @@ -package cn.tursom.core.bytebuffer - -/** - * 缓冲区用完异常 - */ -class OutOfBufferException : Exception() \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/AbstractMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/AbstractMemoryPool.kt new file mode 100644 index 0000000..1f95100 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/AbstractMemoryPool.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/ArrayPool.kt b/src/main/kotlin/cn/tursom/core/pool/ArrayPool.kt deleted file mode 100644 index 47021c7..0000000 --- a/src/main/kotlin/cn/tursom/core/pool/ArrayPool.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/AsyncPool.kt b/src/main/kotlin/cn/tursom/core/pool/AsyncPool.kt deleted file mode 100644 index 8aeea14..0000000 --- a/src/main/kotlin/cn/tursom/core/pool/AsyncPool.kt +++ /dev/null @@ -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() - } -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/CachedLinkedPool.kt b/src/main/kotlin/cn/tursom/core/pool/CachedLinkedPool.kt deleted file mode 100644 index 510c345..0000000 --- a/src/main/kotlin/cn/tursom/core/pool/CachedLinkedPool.kt +++ /dev/null @@ -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) -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/DirectMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/DirectMemoryPool.kt index 8fa760e..76af9b2 100644 --- a/src/main/kotlin/cn/tursom/core/pool/DirectMemoryPool.kt +++ b/src/main/kotlin/cn/tursom/core/pool/DirectMemoryPool.kt @@ -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)" } } \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/ExpandableMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/ExpandableMemoryPool.kt new file mode 100644 index 0000000..d04f13d --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/ExpandableMemoryPool.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/HeapMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/HeapMemoryPool.kt index 020a04f..65e1797 100644 --- a/src/main/kotlin/cn/tursom/core/pool/HeapMemoryPool.kt +++ b/src/main/kotlin/cn/tursom/core/pool/HeapMemoryPool.kt @@ -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)" + } } \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/InstantMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/InstantMemoryPool.kt new file mode 100644 index 0000000..c8b426e --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/InstantMemoryPool.kt @@ -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})" + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/LinkedPool.kt b/src/main/kotlin/cn/tursom/core/pool/LinkedPool.kt index 882caaa..94237c6 100644 --- a/src/main/kotlin/cn/tursom/core/pool/LinkedPool.kt +++ b/src/main/kotlin/cn/tursom/core/pool/LinkedPool.kt @@ -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) } \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/LongBitSetAbstractMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/LongBitSetAbstractMemoryPool.kt new file mode 100644 index 0000000..b401b82 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/LongBitSetAbstractMemoryPool.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/LongBitSetDirectMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/LongBitSetDirectMemoryPool.kt new file mode 100644 index 0000000..77fd120 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/LongBitSetDirectMemoryPool.kt @@ -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)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/LongBitSetHeapMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/LongBitSetHeapMemoryPool.kt new file mode 100644 index 0000000..deb7d59 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/LongBitSetHeapMemoryPool.kt @@ -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)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/MarkedMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/MarkedMemoryPool.kt new file mode 100644 index 0000000..bdf4787 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/MarkedMemoryPool.kt @@ -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() + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/MemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/MemoryPool.kt index 28f3905..3764844 100644 --- a/src/main/kotlin/cn/tursom/core/pool/MemoryPool.kt +++ b/src/main/kotlin/cn/tursom/core/pool/MemoryPool.kt @@ -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) - } -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/Pool.kt b/src/main/kotlin/cn/tursom/core/pool/Pool.kt index 9df8506..49f1d02 100644 --- a/src/main/kotlin/cn/tursom/core/pool/Pool.kt +++ b/src/main/kotlin/cn/tursom/core/pool/Pool.kt @@ -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() + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/ScalabilityMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/ScalabilityMemoryPool.kt new file mode 100644 index 0000000..eae0087 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/ScalabilityMemoryPool.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/ThreadLocalMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/ThreadLocalMemoryPool.kt new file mode 100644 index 0000000..e7dedec --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/ThreadLocalMemoryPool.kt @@ -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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeAbstractMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeAbstractMemoryPool.kt new file mode 100644 index 0000000..b08fc55 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeAbstractMemoryPool.kt @@ -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) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeDirectMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeDirectMemoryPool.kt new file mode 100644 index 0000000..d8d59a7 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeDirectMemoryPool.kt @@ -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)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeHeapMemoryPool.kt b/src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeHeapMemoryPool.kt new file mode 100644 index 0000000..0b93907 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/pool/ThreadUnsafeHeapMemoryPool.kt @@ -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)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/timer/NonLockTaskQueue.kt b/src/main/kotlin/cn/tursom/core/timer/NonLockTaskQueue.kt new file mode 100644 index 0000000..1b8b1a3 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/timer/NonLockTaskQueue.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/timer/StaticWheelTimer.kt b/src/main/kotlin/cn/tursom/core/timer/StaticWheelTimer.kt deleted file mode 100644 index 90b8b87..0000000 --- a/src/main/kotlin/cn/tursom/core/timer/StaticWheelTimer.kt +++ /dev/null @@ -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) } - } -} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/timer/SynchronizedTaskQueue.kt b/src/main/kotlin/cn/tursom/core/timer/SynchronizedTaskQueue.kt new file mode 100644 index 0000000..f4ab237 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/timer/SynchronizedTaskQueue.kt @@ -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 + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/timer/TaskQueue.kt b/src/main/kotlin/cn/tursom/core/timer/TaskQueue.kt new file mode 100644 index 0000000..2803121 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/timer/TaskQueue.kt @@ -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? +} \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/timer/Timer.kt b/src/main/kotlin/cn/tursom/core/timer/Timer.kt index f2d037b..ece3f7c 100644 --- a/src/main/kotlin/cn/tursom/core/timer/Timer.kt +++ b/src/main/kotlin/cn/tursom/core/timer/Timer.kt @@ -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 + } + }) + } } diff --git a/src/main/kotlin/cn/tursom/core/timer/TimerTask.kt b/src/main/kotlin/cn/tursom/core/timer/TimerTask.kt index 1849f8e..4f320ee 100644 --- a/src/main/kotlin/cn/tursom/core/timer/TimerTask.kt +++ b/src/main/kotlin/cn/tursom/core/timer/TimerTask.kt @@ -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() } \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/timer/WheelTimer.kt b/src/main/kotlin/cn/tursom/core/timer/WheelTimer.kt index 48f90ac..55e92be 100644 --- a/src/main/kotlin/cn/tursom/core/timer/WheelTimer.kt +++ b/src/main/kotlin/cn/tursom/core/timer/WheelTimer.kt @@ -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) } } \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/bytebuffer/serialize.kt b/utils/src/main/kotlin/cn/tursom/utils/bytebuffer/serialize.kt index e31d1f7..0f89f6f 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/bytebuffer/serialize.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/bytebuffer/serialize.kt @@ -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) } diff --git a/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncArrayPool.kt b/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncArrayPool.kt deleted file mode 100644 index 5526b60..0000000 --- a/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncArrayPool.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncCachedLinkedPool.kt b/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncCachedLinkedPool.kt deleted file mode 100644 index 4d68466..0000000 --- a/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncCachedLinkedPool.kt +++ /dev/null @@ -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) -} diff --git a/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncLinkedCachePool.kt b/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncLinkedCachePool.kt deleted file mode 100644 index 3881c3a..0000000 --- a/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncLinkedCachePool.kt +++ /dev/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) -} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncMemoryPool.kt b/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncMemoryPool.kt deleted file mode 100644 index 9621ed1..0000000 --- a/utils/src/main/kotlin/cn/tursom/utils/pool/AsyncMemoryPool.kt +++ /dev/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 -} - - diff --git a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyAdvanceByteBuffer.kt b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyAdvanceByteBuffer.kt deleted file mode 100644 index d4429d3..0000000 --- a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyAdvanceByteBuffer.kt +++ /dev/null @@ -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)" - } -} \ No newline at end of file diff --git a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyByteBuffer.kt b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyByteBuffer.kt new file mode 100644 index 0000000..71f2be8 --- /dev/null +++ b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyByteBuffer.kt @@ -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)" + } +} \ No newline at end of file diff --git a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyChunkedByteBuffer.kt b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyChunkedByteBuffer.kt index a247101..438f0d8 100644 --- a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyChunkedByteBuffer.kt +++ b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyChunkedByteBuffer.kt @@ -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() {} } diff --git a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyChunkedInput.kt b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyChunkedInput.kt index 747e728..b8f9081 100644 --- a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyChunkedInput.kt +++ b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyChunkedInput.kt @@ -14,14 +14,14 @@ class NettyChunkedInput(val chunked: Chunked) : ChunkedInput<ByteBuf> { override fun readChunk(ctx: ChannelHandlerContext?): ByteBuf { val buf = chunked.readChunk() - return if (buf is NettyAdvanceByteBuffer) buf.byteBuf - else Unpooled.wrappedBuffer(buf.nioBuffer) + return if (buf is NettyByteBuffer) buf.byteBuf + else Unpooled.wrappedBuffer(buf.readBuffer()) } override fun readChunk(allocator: ByteBufAllocator?): ByteBuf { val buf = chunked.readChunk() - return if (buf is NettyAdvanceByteBuffer) buf.byteBuf - else Unpooled.wrappedBuffer(buf.nioBuffer) + return if (buf is NettyByteBuffer) buf.byteBuf + else Unpooled.wrappedBuffer(buf.readBuffer()) } override fun close() = chunked.close() diff --git a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyExceptionContent.kt b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyExceptionContent.kt index 6af0604..5aab353 100644 --- a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyExceptionContent.kt +++ b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyExceptionContent.kt @@ -1,6 +1,6 @@ package cn.tursom.web.netty -import cn.tursom.core.bytebuffer.AdvanceByteBuffer +import cn.tursom.core.buffer.ByteBuffer import cn.tursom.web.ExceptionContent import io.netty.buffer.Unpooled import io.netty.channel.ChannelHandlerContext @@ -17,9 +17,9 @@ class NettyExceptionContent( ctx.write(Unpooled.wrappedBuffer(bytes, offset, length)) } - override fun write(buffer: AdvanceByteBuffer) { + override fun write(buffer: ByteBuffer) { when (buffer) { - is NettyAdvanceByteBuffer -> ctx.write(buffer.byteBuf) + is NettyByteBuffer -> ctx.write(buffer.byteBuf) else -> write(buffer.getBytes()) } } diff --git a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt index d1fbd6b..f34e902 100644 --- a/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt +++ b/web/netty-web/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt @@ -1,7 +1,7 @@ package cn.tursom.web.netty import cn.tursom.core.buf -import cn.tursom.core.bytebuffer.AdvanceByteBuffer +import cn.tursom.core.buffer.ByteBuffer import cn.tursom.web.AdvanceHttpContent import cn.tursom.web.utils.Chunked import io.netty.buffer.ByteBuf @@ -37,7 +37,7 @@ open class NettyHttpContent( val headers: HttpHeaders get() = msg.headers() protected val paramMap by lazy { RequestParser.parse(msg) } override val cookieMap by lazy { getHeader("Cookie")?.let { decodeCookie(it) } ?: mapOf() } - override val body = msg.content()?.let { NettyAdvanceByteBuffer(it) } + override val body = msg.content()?.let { NettyByteBuffer(it) } val responseMap = HashMap<String, Any>() val responseListMap = HashMap<String, ArrayList<Any>>() @@ -45,7 +45,7 @@ open class NettyHttpContent( override var responseCode: Int = 200 override var responseMessage: String? = null override val method: String get() = httpMethod.name() - val chunkedList = ArrayList<AdvanceByteBuffer>() + val chunkedList = ArrayList<ByteBuffer>() override fun getHeader(header: String): String? { return headers[header] @@ -99,7 +99,7 @@ open class NettyHttpContent( responseBody.write(bytes, offset, size) } - override fun write(buffer: AdvanceByteBuffer) { + override fun write(buffer: ByteBuffer) { buffer.writeTo(responseBody) } @@ -115,8 +115,8 @@ open class NettyHttpContent( finish(Unpooled.wrappedBuffer(buffer, offset, size)) } - override fun finish(buffer: AdvanceByteBuffer) { - if (buffer is NettyAdvanceByteBuffer) { + override fun finish(buffer: ByteBuffer) { + if (buffer is NettyByteBuffer) { finish(buffer.byteBuf) } else { super.finish(buffer) @@ -173,7 +173,7 @@ open class NettyHttpContent( ctx.write(response) } - override fun addChunked(buffer: AdvanceByteBuffer) { + override fun addChunked(buffer: ByteBuffer) { chunkedList.add(buffer) } diff --git a/web/src/main/kotlin/cn/tursom/web/ExceptionContent.kt b/web/src/main/kotlin/cn/tursom/web/ExceptionContent.kt index 156b9f5..b3457a3 100644 --- a/web/src/main/kotlin/cn/tursom/web/ExceptionContent.kt +++ b/web/src/main/kotlin/cn/tursom/web/ExceptionContent.kt @@ -1,13 +1,14 @@ package cn.tursom.web -import cn.tursom.core.bytebuffer.AdvanceByteBuffer +import cn.tursom.core.buffer.ByteBuffer + interface ExceptionContent { val cause: Throwable fun write(message: String) fun write(bytes: ByteArray, offset: Int = 0, length: Int = bytes.size - offset) - fun write(buffer: AdvanceByteBuffer) { + fun write(buffer: ByteBuffer) { write(buffer.getBytes()) } diff --git a/web/src/main/kotlin/cn/tursom/web/HttpContent.kt b/web/src/main/kotlin/cn/tursom/web/HttpContent.kt index f65738f..78c0a51 100644 --- a/web/src/main/kotlin/cn/tursom/web/HttpContent.kt +++ b/web/src/main/kotlin/cn/tursom/web/HttpContent.kt @@ -1,7 +1,7 @@ package cn.tursom.web import cn.tursom.core.buf -import cn.tursom.core.bytebuffer.AdvanceByteBuffer +import cn.tursom.core.buffer.ByteBuffer import cn.tursom.core.count import cn.tursom.core.urlDecode import cn.tursom.web.utils.CacheControl @@ -14,160 +14,164 @@ import java.io.RandomAccessFile import java.net.SocketAddress interface HttpContent { - val uri: String - var responseCode: Int - var responseMessage: String? - val body: AdvanceByteBuffer? - val clientIp: SocketAddress - val method: String - val responseBody: ByteArrayOutputStream - val cookieMap: Map<String, Cookie> - val realIp - get() = getHeader("X-Forwarded-For") ?: clientIp.toString().let { str -> - str.substring(1, str.indexOf(':').let { if (it < 1) str.length else it - 1 }) - } - - fun getHeader(header: String): String? - fun getHeaders(): List<Map.Entry<String, String>> - - fun getParam(param: String): String? - fun getParams(): Map<String, List<String>> - fun getParams(param: String): List<String>? - - operator fun get(name: String) = (getHeader(name) ?: getParam(name))?.urlDecode - - fun setResponseHeader(name: String, value: Any) - fun addResponseHeader(name: String, value: Any) - operator fun set(name: String, value: Any) = setResponseHeader(name, value) - - fun write(message: String) - fun write(byte: Byte) - fun write(bytes: ByteArray, offset: Int = 0, size: Int = 0) - fun write(buffer: AdvanceByteBuffer) - fun reset() - - fun finish() = finish(responseBody.buf, 0, responseBody.count) - fun finish(buffer: ByteArray, offset: Int = 0, size: Int = buffer.size - offset) - fun finish(buffer: AdvanceByteBuffer) = finish(buffer.array, buffer.readOffset, buffer.readAllSize()) - fun finish(code: Int) = finishHtml(code) - - fun finishHtml(code: Int = responseCode) { - responseCode = code - setResponseHeader("content-type", "text/html; charset=UTF-8") - finish() + val uri: String + var responseCode: Int + var responseMessage: String? + val body: ByteBuffer? + val clientIp: SocketAddress + val method: String + val responseBody: ByteArrayOutputStream + val cookieMap: Map<String, Cookie> + val realIp + get() = getHeader("X-Forwarded-For") ?: clientIp.toString().let { str -> + str.substring(1, str.indexOf(':').let { if (it < 1) str.length else it - 1 }) } - fun finishText(code: Int = responseCode) { - responseCode = code - setResponseHeader("content-type", "text/plain; charset=UTF-8") - finish() - } + fun getHeader(header: String): String? + fun getHeaders(): List<Map.Entry<String, String>> - fun finishJson(code: Int = responseCode) { - responseCode = code - setResponseHeader("content-type", "application/json; charset=UTF-8") - finish() - } + fun getParam(param: String): String? + fun getParams(): Map<String, List<String>> + fun getParams(param: String): List<String>? - fun finishHtml(response: ByteArray, code: Int = responseCode) { - responseCode = code - setResponseHeader("content-type", "text/html; charset=UTF-8") - finish(response) - } + operator fun get(name: String) = (getHeader(name) ?: getParam(name))?.urlDecode - fun finishText(response: ByteArray, code: Int = responseCode) { - responseCode = code - setResponseHeader("content-type", "text/plain; charset=UTF-8") - finish(response) - } + fun setResponseHeader(name: String, value: Any) + fun addResponseHeader(name: String, value: Any) + operator fun set(name: String, value: Any) = setResponseHeader(name, value) - fun finishJson(response: ByteArray, code: Int = responseCode) { - responseCode = code - setResponseHeader("content-type", "application/json; charset=UTF-8") - finish(response) - } + fun write(message: String) + fun write(byte: Byte) + fun write(bytes: ByteArray, offset: Int = 0, size: Int = 0) + fun write(buffer: ByteBuffer) + fun reset() - fun finishHtml(response: AdvanceByteBuffer, code: Int = responseCode) { - responseCode = code - setResponseHeader("content-type", "text/html; charset=UTF-8") - finish(response) - } + fun finish() = finish(responseBody.buf, 0, responseBody.count) + fun finish(buffer: ByteArray, offset: Int = 0, size: Int = buffer.size - offset) + fun finish(buffer: ByteBuffer) { + finish(buffer.array, buffer.readOffset, buffer.readAllSize()) + buffer.close() + } - fun finishText(response: AdvanceByteBuffer, code: Int = responseCode) { - responseCode = code - setResponseHeader("content-type", "text/plain; charset=UTF-8") - finish(response) - } + fun finish(code: Int) = finishHtml(code) - fun finishJson(response: AdvanceByteBuffer, code: Int = responseCode) { - responseCode = code - setResponseHeader("content-type", "application/json; charset=UTF-8") - finish(response) - } + fun finishHtml(code: Int = responseCode) { + responseCode = code + setResponseHeader("content-type", "text/html; charset=UTF-8") + finish() + } - fun usingCache() = finish(304) + fun finishText(code: Int = responseCode) { + responseCode = code + setResponseHeader("content-type", "text/plain; charset=UTF-8") + finish() + } - fun setCacheTag(tag: Any) = setResponseHeader("Etag", tag) - fun getCacheTag(): String? = getHeader("If-None-Match") + fun finishJson(code: Int = responseCode) { + responseCode = code + setResponseHeader("content-type", "application/json; charset=UTF-8") + finish() + } - fun cacheControl( - cacheControl: CacheControl, - maxAge: Int? = null, - mustRevalidate: Boolean = false - ) = setResponseHeader( - "Cache-Control", "$cacheControl${ - if (maxAge != null && maxAge > 0) ", max-age=$maxAge" else ""}${ - if (mustRevalidate) ", must-revalidate" else "" - }" - ) + fun finishHtml(response: ByteArray, code: Int = responseCode) { + responseCode = code + setResponseHeader("content-type", "text/html; charset=UTF-8") + finish(response) + } - fun getCookie(name: String): Cookie? = cookieMap?.get(name) + fun finishText(response: ByteArray, code: Int = responseCode) { + responseCode = code + setResponseHeader("content-type", "text/plain; charset=UTF-8") + finish(response) + } - fun addCookie( - name: String, - value: Any, - maxAge: Int = 0, - domain: String? = null, - path: String? = null, - sameSite: SameSite? = null - ) = addResponseHeader( - "Set-Cookie", - "$name=$value${ - if (maxAge > 0) "; Max-Age=$maxAge" else ""}${ - if (domain != null) "; Domain=$domain" else ""}${ - if (path != null) "; Path=$path" else ""}${ - if (sameSite != null) ": SameSite=$sameSite" else "" - }" - ) + fun finishJson(response: ByteArray, code: Int = responseCode) { + responseCode = code + setResponseHeader("content-type", "application/json; charset=UTF-8") + finish(response) + } - fun setCookie( - name: String, - value: Any, - maxAge: Int = 0, - domain: String? = null, - path: String? = null, - sameSite: SameSite? = null - ) { - deleteCookie(name, path ?: "/") - addCookie(name, value, maxAge, domain, path, sameSite) - } + fun finishHtml(response: ByteBuffer, code: Int = responseCode) { + responseCode = code + setResponseHeader("content-type", "text/html; charset=UTF-8") + finish(response) + } - fun deleteCookie(name: String, path: String = "/") = - addCookie(name, "deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT", path = path) + fun finishText(response: ByteBuffer, code: Int = responseCode) { + responseCode = code + setResponseHeader("content-type", "text/plain; charset=UTF-8") + finish(response) + } - fun writeChunkedHeader() - fun addChunked(buffer: AdvanceByteBuffer) - fun finishChunked() + fun finishJson(response: ByteBuffer, code: Int = responseCode) { + responseCode = code + setResponseHeader("content-type", "application/json; charset=UTF-8") + finish(response) + } - fun finishChunked(chunked: Chunked) + fun usingCache() = finish(304) - fun finishFile(file: File, chunkSize: Int = 8192) - fun finishFile( - file: RandomAccessFile, - offset: Long = 0, - length: Long = file.length() - offset, - chunkSize: Int = 8192 - ) + fun setCacheTag(tag: Any) = setResponseHeader("Etag", tag) + fun getCacheTag(): String? = getHeader("If-None-Match") - fun setContextType(type: Any) = setResponseHeader("Content-Type", type) + fun cacheControl( + cacheControl: CacheControl, + maxAge: Int? = null, + mustRevalidate: Boolean = false + ) = setResponseHeader( + "Cache-Control", "$cacheControl${ + if (maxAge != null && maxAge > 0) ", max-age=$maxAge" else ""}${ + if (mustRevalidate) ", must-revalidate" else "" + }" + ) + + fun getCookie(name: String): Cookie? = cookieMap[name] + + fun addCookie( + name: String, + value: Any, + maxAge: Int = 0, + domain: String? = null, + path: String? = null, + sameSite: SameSite? = null + ) = addResponseHeader( + "Set-Cookie", + "$name=$value${ + if (maxAge > 0) "; Max-Age=$maxAge" else ""}${ + if (domain != null) "; Domain=$domain" else ""}${ + if (path != null) "; Path=$path" else ""}${ + if (sameSite != null) ": SameSite=$sameSite" else "" + }" + ) + + fun setCookie( + name: String, + value: Any, + maxAge: Int = 0, + domain: String? = null, + path: String? = null, + sameSite: SameSite? = null + ) { + deleteCookie(name, path ?: "/") + addCookie(name, value, maxAge, domain, path, sameSite) + } + + fun deleteCookie(name: String, path: String = "/") = + addCookie(name, "deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT", path = path) + + fun writeChunkedHeader() + fun addChunked(buffer: ByteBuffer) + fun finishChunked() + + fun finishChunked(chunked: Chunked) + + fun finishFile(file: File, chunkSize: Int = 8192) + fun finishFile( + file: RandomAccessFile, + offset: Long = 0, + length: Long = file.length() - offset, + chunkSize: Int = 8192 + ) + + fun setContextType(type: Any) = setResponseHeader("Content-Type", type) } \ No newline at end of file diff --git a/web/src/main/kotlin/cn/tursom/web/utils/Chunked.kt b/web/src/main/kotlin/cn/tursom/web/utils/Chunked.kt index d98366d..2705ef6 100644 --- a/web/src/main/kotlin/cn/tursom/web/utils/Chunked.kt +++ b/web/src/main/kotlin/cn/tursom/web/utils/Chunked.kt @@ -1,11 +1,11 @@ package cn.tursom.web.utils -import cn.tursom.core.bytebuffer.AdvanceByteBuffer +import cn.tursom.core.buffer.ByteBuffer interface Chunked { val progress: Long val length: Long val endOfInput: Boolean - fun readChunk(): AdvanceByteBuffer + fun readChunk(): ByteBuffer fun close() } \ No newline at end of file diff --git a/web/src/main/kotlin/cn/tursom/web/utils/EmptyHttpContent.kt b/web/src/main/kotlin/cn/tursom/web/utils/EmptyHttpContent.kt index ac492b4..c089f58 100644 --- a/web/src/main/kotlin/cn/tursom/web/utils/EmptyHttpContent.kt +++ b/web/src/main/kotlin/cn/tursom/web/utils/EmptyHttpContent.kt @@ -1,6 +1,6 @@ package cn.tursom.web.utils -import cn.tursom.core.bytebuffer.AdvanceByteBuffer +import cn.tursom.core.buffer.ByteBuffer import cn.tursom.web.HttpContent import java.io.ByteArrayOutputStream import java.io.File @@ -9,38 +9,38 @@ import java.net.InetSocketAddress import java.net.SocketAddress class EmptyHttpContent( - override val uri: String = "/", - override var responseCode: Int = 200, - override var responseMessage: String? = null, - override val body: AdvanceByteBuffer? = null, - override val clientIp: SocketAddress = InetSocketAddress(0), - override val method: String = "GET", - override val responseBody: ByteArrayOutputStream = ByteArrayOutputStream(0), - override val cookieMap: Map<String, Cookie> = mapOf() + override val uri: String = "/", + override var responseCode: Int = 200, + override var responseMessage: String? = null, + override val body: ByteBuffer? = null, + override val clientIp: SocketAddress = InetSocketAddress(0), + override val method: String = "GET", + override val responseBody: ByteArrayOutputStream = ByteArrayOutputStream(0), + override val cookieMap: Map<String, Cookie> = mapOf() ) : HttpContent { - override fun getHeader(header: String): String? = null - override fun getHeaders(): List<Map.Entry<String, String>> = listOf() - override fun getParam(param: String): String? = null - override fun getParams(): Map<String, List<String>> = mapOf() - override fun getParams(param: String): List<String>? = null - override fun setResponseHeader(name: String, value: Any) {} - override fun addResponseHeader(name: String, value: Any) {} - override fun write(message: String) {} - override fun write(byte: Byte) {} - override fun write(bytes: ByteArray, offset: Int, size: Int) {} - override fun write(buffer: AdvanceByteBuffer) {} - override fun reset() {} - override fun finish() {} - override fun finish(buffer: ByteArray, offset: Int, size: Int) {} - override fun finish(code: Int) {} - override fun finishHtml(code: Int) {} - override fun finishText(code: Int) {} - override fun finishJson(code: Int) {} - override fun writeChunkedHeader() {} - override fun addChunked(buffer: AdvanceByteBuffer) {} - override fun finishChunked() {} - override fun finishChunked(chunked: Chunked) {} - override fun finishFile(file: File, chunkSize: Int) {} - override fun finishFile(file: RandomAccessFile, offset: Long, length: Long, chunkSize: Int) {} + override fun getHeader(header: String): String? = null + override fun getHeaders(): List<Map.Entry<String, String>> = listOf() + override fun getParam(param: String): String? = null + override fun getParams(): Map<String, List<String>> = mapOf() + override fun getParams(param: String): List<String>? = null + override fun setResponseHeader(name: String, value: Any) {} + override fun addResponseHeader(name: String, value: Any) {} + override fun write(message: String) {} + override fun write(byte: Byte) {} + override fun write(bytes: ByteArray, offset: Int, size: Int) {} + override fun write(buffer: ByteBuffer) {} + override fun reset() {} + override fun finish() {} + override fun finish(buffer: ByteArray, offset: Int, size: Int) {} + override fun finish(code: Int) {} + override fun finishHtml(code: Int) {} + override fun finishText(code: Int) {} + override fun finishJson(code: Int) {} + override fun writeChunkedHeader() {} + override fun addChunked(buffer: ByteBuffer) {} + override fun finishChunked() {} + override fun finishChunked(chunked: Chunked) {} + override fun finishFile(file: File, chunkSize: Int) {} + override fun finishFile(file: RandomAccessFile, offset: Long, length: Long, chunkSize: Int) {} }