All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.qifun.bcp.BcpIo.scala Maven / Gradle / Ivy

/*
 * scala-bcp
 * Copyright 2014 深圳岂凡网络有限公司 (Shenzhen QiFun Network Corp., LTD)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.qifun.bcp

import java.io.EOFException
import java.io.IOException
import java.nio.ByteBuffer
import scala.annotation.tailrec
import com.qifun.bcp.Bcp._
import com.qifun.statelessFuture.Future
import com.qifun.statelessFuture.util.io.SocketInputStream
import com.qifun.statelessFuture.util.io.SocketWritingQueue
import java.io.EOFException
import java.io.IOException
import scala.collection.mutable.ArrayBuffer

private[bcp] object BcpIo {

  private final def receiveUnsignedVarint(stream: SocketInputStream): Future[Int] = {
    def receiveRestBytes(result: Int, i: Int): Future[Int] = Future[Int] {
      (stream.available_=(1)).await
      stream.read() match {
        case -1 => throw new EOFException
        case b => {
          if (i < 32) {
            if (b >= 0x80) {
              receiveRestBytes(
                result | ((b & 0x7f) << i),
                i + 7).await
            } else {
              result | (b << i)
            }
          } else {
            throw new BcpException.VarintTooBig
          }

        }
      }
    }
    receiveRestBytes(0, 0)
  }

  @tailrec
  private final def writeUnsignedVarint(buffer: ByteBuffer, value: Int) {
    if ((value & 0xFFFFFF80) == 0) {
      buffer.put(value.toByte)
      return ;
    } else {
      buffer.put(((value & 0x7F) | 0x80).toByte)
      writeUnsignedVarint(buffer, value >>> 7)
    }
  }

  final def enqueue(queue: SocketWritingQueue, pack: Acknowledge.type) {
    queue.enqueue(ByteBuffer.wrap(Array[Byte](Acknowledge.HeadByte)))
  }

  final def enqueue(queue: SocketWritingQueue, pack: Finish.type) {
    queue.enqueue(ByteBuffer.wrap(Array[Byte](Finish.HeadByte)))
  }

  final def enqueue(queue: SocketWritingQueue, pack: RetransmissionFinish) {
    val headBuffer = ByteBuffer.allocate(20)
    headBuffer.put(RetransmissionFinish.HeadByte)
    writeUnsignedVarint(headBuffer, pack.connectionId)
    writeUnsignedVarint(headBuffer, pack.packId)
    headBuffer.flip()
    queue.enqueue(headBuffer)
  }

  final def enqueue(queue: SocketWritingQueue, pack: RetransmissionData) {
    val headBuffer = ByteBuffer.allocate(20)
    headBuffer.put(RetransmissionData.HeadByte)
    writeUnsignedVarint(headBuffer, pack.connectionId)
    writeUnsignedVarint(headBuffer, pack.packId)
    writeUnsignedVarint(headBuffer, pack.buffers.view.map(_.remaining).sum)
    headBuffer.flip()
    val newBuffer = for (buffer <- pack.buffers) yield buffer.duplicate()
    queue.enqueue((headBuffer +: newBuffer.view): _*)
  }

  final def enqueue(queue: SocketWritingQueue, pack: Data) {
    val headBuffer = ByteBuffer.allocate(20)
    headBuffer.put(Data.HeadByte)
    writeUnsignedVarint(headBuffer, pack.buffers.view.map(_.remaining).sum)
    headBuffer.flip()
    val newBuffer = for (buffer <- pack.buffers) yield buffer.duplicate()
    queue.enqueue((headBuffer +: newBuffer.view): _*)
  }

  final def enqueue(queue: SocketWritingQueue, pack: ShutDown.type) {
    queue.enqueue(ByteBuffer.wrap(Array[Byte](ShutDown.HeadByte)))
  }

  final def enqueue(queue: SocketWritingQueue, pack: HeartBeat.type) {
    queue.enqueue(ByteBuffer.wrap(Array[Byte](HeartBeat.HeadByte)))
  }

  final def enqueue(queue: SocketWritingQueue, pack: Packet) {
    pack match {
      case pack @ Acknowledge => {
        enqueue(queue, pack)
      }
      case pack: Data => {
        enqueue(queue, pack)
      }
      case pack @ Finish => {
        enqueue(queue, pack)
      }
      case pack: RetransmissionData => {
        enqueue(queue, pack)
      }
      case pack: RetransmissionFinish => {
        enqueue(queue, pack)
      }
      case pack @ ShutDown => {
        enqueue(queue, pack)
      }
      case pack @ HeartBeat => {
        enqueue(queue, pack)
      }
    }
  }

  @throws(classOf[IOException])
  final def receive(stream: SocketInputStream) = Future[ClientToServer] {
    stream.available_=(1).await
    stream.read() match {
      case Data.HeadByte => {
        val length = receiveUnsignedVarint(stream).await
        if (length > MaxDataSize) {
          throw new BcpException.DataTooBig
        }
        stream.available_=(length).await
        val buffer = new ArrayBuffer[ByteBuffer]
        stream.move(buffer, length)
        Data(buffer)
      }
      case RetransmissionData.HeadByte => {
        val connectionId = receiveUnsignedVarint(stream).await
        val packId = receiveUnsignedVarint(stream).await
        val length = receiveUnsignedVarint(stream).await
        if (length > MaxDataSize) {
          throw new BcpException.DataTooBig
        }
        stream.available_=(length).await
        val buffer = new ArrayBuffer[ByteBuffer]
        stream.move(buffer, length)
        RetransmissionData(connectionId, packId, buffer)
      }
      case RetransmissionFinish.HeadByte => {
        val connectionId = receiveUnsignedVarint(stream).await
        val packId = receiveUnsignedVarint(stream).await
        RetransmissionFinish(connectionId, packId)
      }
      case Acknowledge.HeadByte => {
        Acknowledge
      }
      case Finish.HeadByte => {
        Finish
      }
      case ShutDown.HeadByte => {
        ShutDown
      }
      case HeartBeat.HeadByte => {
        HeartBeat
      }
      case _ => throw new BcpException.UnknownHeadByte
    }

  }

  private def boolToInt(bool: Boolean) = if (bool) 1 else 0
  private def intToBool(int: Int) = if (int == 0) false else true

  final def receiveHead(stream: SocketInputStream) = Future {
    val sessionId = receiveSessionId(stream).await
    val isRenew = receiveUnsignedVarint(stream).await
    val connectionId = receiveUnsignedVarint(stream).await
    ConnectionHead(sessionId, intToBool(isRenew), connectionId)
  }

  private def receiveSessionId(stream: SocketInputStream) = Future[Array[Byte]] {
    stream.available_=(NumBytesSessionId).await
    var sessionId = Array.ofDim[Byte](NumBytesSessionId)
    stream.read(sessionId)
    sessionId
  }

  final def enqueueHead(stream: SocketWritingQueue, head: ConnectionHead) = {
    val ConnectionHead(sessionId, isRenew, connectionId) = head
    val headBuffer = ByteBuffer.allocate(NumBytesSessionId + 1 + 5)
    headBuffer.put(sessionId)
    writeUnsignedVarint(headBuffer, boolToInt(isRenew))
    writeUnsignedVarint(headBuffer, connectionId)
    headBuffer.flip()
    stream.enqueue(headBuffer)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy