add compiling proto files and building of proto lib to gradle. maked refactoring

This commit is contained in:
MaximZaitsev
2016-07-20 14:10:20 +03:00
parent 8b7ebd6805
commit 4f36e58dd1
13 changed files with 971 additions and 1748 deletions
+15 -2
View File
@@ -17,13 +17,26 @@ apply plugin: 'kotlin'
sourceCompatibility = 1.5
task compileProto(type: Exec) {
executable "./compileProto.sh"
}
task buildProtoLib(type: Copy) {
//just copy lib src to project
def pathToProtoKotSrc = "../../../proto/compiler/src/"
from pathToProtoKotSrc + "CodedInputStream.kt", pathToProtoKotSrc + "CodedOutputStream.kt",
pathToProtoKotSrc + "WireFormat.kt", pathToProtoKotSrc + "WireType.kt",
pathToProtoKotSrc + "InvalidProtocolBufferException.kt"
into "src/main/java"
}
task getDeps(type: Copy) {
from sourceSets.main.compileClasspath
into 'build/libs'
}
build.dependsOn getDeps
compileKotlin.dependsOn compileProto
compileProto.dependsOn buildProtoLib
repositories {
mavenCentral()
}
@@ -42,7 +55,7 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "io.netty:netty-all:4.1.2.Final"
compile "com.google.protobuf:protobuf-java:3.0.0-beta-3"
compile group: "com.martiansoftware",name:"jsap",version:"2.1"
compile group: "com.martiansoftware", name: "jsap", version: "2.1"
testCompile group: 'junit', name: 'junit', version: '4.11'
+1
View File
@@ -0,0 +1 @@
../../../proto/compiler/google/src/google/protobuf/compiler/kotlin/protoc --kotlin_out="./src/main/java" --proto_path="../../../proto/server_car" ../../../proto/server_car/Direction.proto
+8 -22
View File
@@ -1,8 +1,7 @@
import client.Client
import io.netty.buffer.Unpooled
import io.netty.handler.codec.http.*
import proto.car.RouteP
import proto.car.RouteP.Direction.Command
import java.io.ByteArrayOutputStream
/**
* Created by user on 7/14/16.
@@ -15,27 +14,14 @@ class CarControl constructor(client: Client) {
this.client = client
}
fun executeCommand(direction: Char) {
fun executeCommand(command: DirectionRequest.Command) {
val directionBuilder = RouteP.Direction.newBuilder()
when (direction) {
'w' -> {
directionBuilder.setCommand(Command.forward)
}
's' -> {
directionBuilder.setCommand(Command.backward)
}
'd' -> {
directionBuilder.setCommand(Command.right)
}
'a' -> {
directionBuilder.setCommand(Command.left)
}
'h' -> {
directionBuilder.setCommand(Command.stop)
}
}
val request = DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/control", Unpooled.copiedBuffer(directionBuilder.build().toByteArray()));
val directionBuilder = DirectionRequest.BuilderDirectionRequest()
directionBuilder.setCommand(command)
val byteArrayStream = ByteArrayOutputStream()
directionBuilder.build().writeTo(CodedOutputStream(byteArrayStream))
val request = DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/control", Unpooled.copiedBuffer(byteArrayStream.toByteArray()));
request.headers().set(HttpHeaderNames.HOST, client.host)
request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE)
request.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes())
@@ -0,0 +1,305 @@
import java.nio.ByteBuffer
import java.nio.ByteOrder
import WireFormat.VARINT_INFO_BITS_COUNT
import WireFormat.VARINT_INFO_BITS_MASK
import WireFormat.VARINT_UTIL_BIT_MASK
/**
* Created by Dmitry Savvinov on 7/6/16.
*
* Hides details of work with Protobuf encoding
*
* Note that CodedInputStream reads protobuf-defined types from stream (such as int32, sint32, etc),
* while CodedOutputStream has methods for writing Kotlin-types (such as Boolean, Int, Long, Short, etc)
*
*/
// TODO: refactor correctness checks into readTag
class CodedInputStream(input: java.io.InputStream) {
val bufferedInput: java.io.BufferedInputStream
init {
bufferedInput = java.io.BufferedInputStream(input) // TODO: Java's realization uses hand-written buffers. Why?
}
fun readInt32(expectedFieldNumber: Int): Int {
val tag = readTag(expectedFieldNumber, WireType.VARINT)
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
return readInt32NoTag()
}
// Note that unsigned integer types are stored as their signed counterparts with top bit
// simply stored in the sign bit - similar to Java's protobuf implementation. Hence, all
// methods reading unsigned ints simply redirect call to corresponding signed-reading method
fun readUInt32(expectedFieldNumber: Int): Int {
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readUInt32NoTag()
}
fun readUInt32NoTag(): Int {
return readInt32NoTag()
}
fun readInt64(expectedFieldNumber: Int): Long {
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readInt64NoTag()
}
// See note on unsigned integers implementations above
fun readUInt64(expectedFieldNumber: Int): Long {
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readUInt64NoTag()
}
fun readUInt64NoTag(): Long {
return readInt64NoTag()
}
fun readBool(expectedFieldNumber: Int): Boolean {
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readBoolNoTag()
}
fun readBoolNoTag(): Boolean {
val readValue = readInt32NoTag()
val boolValue = when (readValue) {
0 -> false
1 -> true
else -> throw InvalidProtocolBufferException("Expected boolean-encoding (1 or 0), got $readValue")
}
return boolValue
}
// Reading enums is like reading one int32 number. Caller is responsible for converting this ordinal to enum-object
fun readEnum(expectedFieldNumber: Int): Int {
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readEnumNoTag()
}
fun readEnumNoTag(): Int {
return readUInt32NoTag()
}
fun readSInt32(expectedFieldNumber: Int): Int {
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readSInt32NoTag()
}
fun readSInt32NoTag(): Int {
return readZigZag32NoTag()
}
fun readSInt64(expectedFieldNumber: Int): Long {
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readZigZag64NoTag()
}
fun readFixed32(expectedFieldNumber: Int): Int {
val tag = readTag(expectedFieldNumber, WireType.FIX_32)
return readFixed32NoInt()
}
fun readFixed32NoInt(): Int {
return readLittleEndianInt()
}
fun readSFixed32(expectedFieldNumber: Int): Int {
val tag = readTag(expectedFieldNumber, WireType.FIX_32)
return readSFixed32NoTag()
}
fun readSFixed32NoTag(): Int {
return readLittleEndianInt()
}
fun readFixed64(expectedFieldNumber: Int): Long {
val tag = readTag(expectedFieldNumber, WireType.FIX_64)
return readFixed64NoTag()
}
fun readFixed64NoTag(): Long {
return readLittleEndianLong()
}
fun readSFixed64(expectedFieldNumber: Int): Long {
val tag = readTag(expectedFieldNumber, WireType.FIX_64)
return readSFixed64NoTag()
}
fun readSFixed64NoTag(): Long {
return readLittleEndianLong()
}
fun readDouble(expectedFieldNumber: Int): Double {
val tag = readTag(expectedFieldNumber, WireType.FIX_64)
return readDoubleNoTag()
}
fun readDoubleNoTag(): Double {
return readLittleEndianDouble()
}
fun readFloat(expectedFieldNumber: Int): Float {
val tag = readTag(expectedFieldNumber, WireType.FIX_32)
return readFloatNoTag()
}
fun readFloatNoTag(): Float {
return readLittleEndianFloat()
}
fun readString(expectedFieldNumber: Int): String {
val tag = readTag(expectedFieldNumber, WireType.LENGTH_DELIMITED)
return readStringNoTag()
}
fun readStringNoTag(): String {
val length = readInt32NoTag()
val value = String(readRawBytes(length))
return value
}
fun readBytes(expectedFieldNumber: Int): ByteArray {
val tag = readTag(expectedFieldNumber, WireType.LENGTH_DELIMITED)
return readBytesNoTag()
}
fun readBytesNoTag(): ByteArray {
val length = readInt32NoTag()
return readRawBytes(length)
}
/** ============ Utility methods ==================
* They are left non-private for cases when one wants to implement her/his own protocol format.
* Then she/he can re-use low-level methods for operating with raw values, that are not annotated with Protobuf tags.
*/
fun checkFieldCorrectness(
expectedFieldNumber: Int,
actualFieldNumber: Int,
expectedWireType: WireType,
actualWireType: WireType) {
if (expectedFieldNumber != actualFieldNumber) {
throw InvalidProtocolBufferException(
"Error in protocol format: \n " +
"Expected field number ${expectedFieldNumber}, got ${actualFieldNumber}")
}
if (expectedWireType != actualWireType) {
throw InvalidProtocolBufferException("Error in protocol format: \n " +
"Expected ${expectedWireType.name} type, got ${actualWireType.name}")
}
}
fun readLittleEndianDouble(): Double {
val byteBuffer = ByteBuffer.wrap(readRawBytes(8))
byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
return byteBuffer.getDouble(0)
}
fun readLittleEndianFloat(): Float {
val byteBuffer = ByteBuffer.wrap(readRawBytes(4))
byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
return byteBuffer.getFloat(0)
}
fun readLittleEndianInt(): Int {
val byteBuffer = ByteBuffer.wrap(readRawBytes(8))
byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
return byteBuffer.getInt(0)
}
fun readLittleEndianLong(): Long {
val byteBuffer = ByteBuffer.wrap(readRawBytes(4))
byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
return byteBuffer.getLong(0)
}
fun readRawBytes(count: Int): ByteArray {
val ba = ByteArray(count)
for (i in 0..(count - 1)) {
ba[i] = bufferedInput.read().toByte()
}
return ba
}
// reads tag. Note that it returns 0 for the end of message!
fun readTag(expectedFieldNumber: Int, expectedWireType: WireType): Int {
if (isAtEnd()) {
return 0 // we can safely return 0 as sign of end of message, because 0-tags are illegal
}
val tag = readInt32NoTag()
if (tag == 0) { // if we somehow had read 0-tag, then message is corrupted
throw InvalidProtocolBufferException("Invalid tag 0")
}
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, expectedWireType, actualWireType)
return tag
}
// reads varint not larger than 32-bit integer according to protobuf varint-encoding
fun readInt32NoTag(): Int {
var done: Boolean = false
var result: Int = 0
var step: Int = 0
while (!done) {
val byte: Int = bufferedInput.read()
result = result or
(
(byte and VARINT_INFO_BITS_MASK)
shl
(VARINT_INFO_BITS_COUNT * step)
)
step++
if ((byte and VARINT_UTIL_BIT_MASK) == 0) {
done = true
}
}
return result
}
// reads varint not larger than 64-bit integer according to protobuf varint-encoding
fun readInt64NoTag(): Long {
var done: Boolean = false
var result: Long = 0
var step: Int = 0
while (!done) {
val byte: Int = bufferedInput.read()
result = result or
(
(byte and VARINT_INFO_BITS_MASK).toLong()
shl
(VARINT_INFO_BITS_COUNT * step)
)
step++
if ((byte and VARINT_UTIL_BIT_MASK) == 0 || byte == -1) {
done = true
}
}
return result
}
// reads zig-zag encoded integer not larger than 32-bit long
fun readZigZag32NoTag(): Int {
val value = readInt32NoTag()
return (value shr 1) xor (-(value and 1)) // bit magic for decoding zig-zag number
}
// reads zig-zag encoded integer not larger than 64-bit long
fun readZigZag64NoTag(): Long {
val value = readInt64NoTag()
return (value shr 1) xor (-(value and 1L)) // bit magic for decoding zig-zag number
}
// checks if at least one more byte can be read from underlying input stream
fun isAtEnd(): Boolean {
bufferedInput.mark(1)
val byte = bufferedInput.read()
bufferedInput.reset()
return byte == -1
}
}
@@ -0,0 +1,258 @@
import java.nio.ByteBuffer
import java.nio.ByteOrder
import WireFormat.VARINT_INFO_BITS_COUNT
import WireFormat.VARINT_INFO_BITS_MASK
import WireFormat.VARINT_UTIL_BIT_MASK
/**
* Created by user on 7/6/16.
*/
class CodedOutputStream(val output: java.io.OutputStream) {
fun writeTag(fieldNumber: Int, type: WireType) {
val tag = (fieldNumber shl 3) or type.ordinal
writeInt32NoTag(tag)
}
fun writeInt32(fieldNumber: Int, value: Int?) {
value ?: return
writeTag(fieldNumber, WireType.VARINT)
writeInt32NoTag(value)
}
// Note that unsigned integer types are stored as their signed counterparts with top bit
// simply stored in the sign bit - similar to Java's protobuf implementation. Hence, all
// methods, writing unsigned ints simply redirect call to corresponding signed-writing method
fun writeUInt32(fieldNumber: Int, value: Int?) {
value ?: return
writeInt32(fieldNumber, value)
}
fun writeInt64(fieldNumber: Int, value: Long?) {
value ?: return
writeTag(fieldNumber, WireType.VARINT)
writeInt64NoTag(value)
}
// See notes on unsigned integers implementation above
fun writeUInt64(fieldNumber: Int, value: Long?) {
value ?: return
writeInt64(fieldNumber, value)
}
fun writeBool(fieldNumber: Int, value: Boolean?) {
value ?: return
writeTag(fieldNumber, WireType.VARINT)
writeBoolNoTag(value)
}
fun writeBoolNoTag(value: Boolean) {
writeInt32NoTag(if (value) 1 else 0)
}
// Writing enums is like writing one int32 number. Caller is responsible for converting enum-object to ordinal
fun writeEnum(fieldNumber: Int, value: Int?) {
value ?: return
writeTag(fieldNumber, WireType.VARINT)
writeEnumNoTag(value)
}
fun writeEnumNoTag(value: Int) {
writeInt32NoTag(value)
}
fun writeSInt32(fieldNumber: Int, value: Int?) {
value ?: return
writeTag(fieldNumber, WireType.VARINT)
writeSInt32NoTag(value)
}
fun writeSInt32NoTag(value: Int) {
writeInt32NoTag((value shl 1) xor (value shr 31))
}
fun writeSInt64(fieldNumber: Int, value: Long?) {
value ?: return
writeTag(fieldNumber, WireType.VARINT)
writeSInt64NoTag(value)
}
fun writeSInt64NoTag(value: Long) {
writeInt64NoTag((value shl 1) xor (value shr 63))
}
fun writeFixed32(fieldNumber: Int, value: Int?) {
value ?: return
writeTag(fieldNumber, WireType.FIX_32)
writeFixed32NoTag(value)
}
fun writeFixed32NoTag(value: Int) {
writeLittleEndian(value)
}
// See notes on unsigned integers implementation above
fun writeSFixed32(fieldNumber: Int, value: Int?) {
value ?: return
writeTag(fieldNumber, WireType.FIX_32)
writeSFixed32NoTag(value)
}
fun writeSFixed32NoTag(value: Int) {
writeLittleEndian(value)
}
fun writeFixed64(fieldNumber: Int, value: Long?) {
value ?: return
writeTag(fieldNumber, WireType.FIX_64)
writeFixed64NoTag(value)
}
fun writeFixed64NoTag(value: Long) {
writeLittleEndian(value)
}
// See notes on unsigned integers implementation above
fun writeSFixed64(fieldNumber: Int, value: Long?) {
value ?: return
writeTag(fieldNumber, WireType.FIX_64)
writeSFixed64NoTag(value)
}
fun writeSFixed64NoTag(value: Long) {
writeLittleEndian(value)
}
fun writeDouble(fieldNumber: Int, value: Double?) {
value ?: return
writeTag(fieldNumber, WireType.FIX_64)
writeDoubleNoTag(value)
}
fun writeDoubleNoTag(value: Double) {
writeLittleEndian(value)
}
fun writeFloat(fieldNumber: Int, value: Float?) {
value ?: return
writeTag(fieldNumber, WireType.FIX_32)
writeFloatNoTag(value)
}
fun writeFloatNoTag(value: Float) {
writeLittleEndian(value)
}
fun writeString(fieldNumber: Int, value: String?) {
value ?: return
writeTag(fieldNumber, WireType.LENGTH_DELIMITED)
writeStringNoTag(value)
}
fun writeStringNoTag(value: String) {
writeInt32NoTag(value.length)
output.write(value.toByteArray(Charsets.UTF_8))
}
fun writeBytes(fieldNumber: Int, value: ByteArray?) {
value ?: return
writeTag(fieldNumber, WireType.LENGTH_DELIMITED)
writeBytesNoTag(value)
}
fun writeBytesNoTag(value: ByteArray) {
writeInt32NoTag(value.size)
output.write(value)
}
/** ============ Utility methods ==================
* They are left non-private for cases when one wants to implement her/his own protocol format.
* Then she/he can re-use low-level methods for operating with raw values, that are not annotated with Protobuf tags.
*/
fun writeLittleEndian(value: Int?) {
value ?: return
val bb = ByteBuffer.allocate(4)
bb.order(ByteOrder.LITTLE_ENDIAN)
bb.putInt(value)
output.write(bb.array())
}
fun writeLittleEndian(value: Long?) {
value ?: return
val bb = ByteBuffer.allocate(8)
bb.order(ByteOrder.LITTLE_ENDIAN)
bb.putLong(value)
output.write(bb.array())
}
fun writeLittleEndian(value: Double?) {
value ?: return
val bb = ByteBuffer.allocate(8)
bb.order(ByteOrder.LITTLE_ENDIAN)
bb.putDouble(value)
output.write(bb.array())
}
fun writeLittleEndian(value: Float?) {
value ?: return
val bb = ByteBuffer.allocate(4)
bb.order(ByteOrder.LITTLE_ENDIAN)
bb.putFloat(value)
output.write(bb.array())
}
fun writeInt32NoTag(value: Int?) {
value ?: return
var curValue: Int = value
// we have at most 32 information bits. With overhead of 1 bit per 7 bits we need at most 5 bytes for encoding
val res = ByteArray(5)
var resSize = 0
do {
// encode current 7 bits
var curByte = (curValue and WireFormat.VARINT_INFO_BITS_MASK)
// discard encoded bits. Note that unsigned shift is needed for cases with negative numbers
curValue = curValue ushr VARINT_INFO_BITS_COUNT
// check if there will be next byte in encoding and set util bit if needed
if (curValue != 0) {
curByte = curByte or VARINT_UTIL_BIT_MASK
}
res[resSize] = curByte.toByte()
resSize++
} while(curValue != 0)
output.write(res, 0, resSize)
}
fun writeInt64NoTag(value: Long?) {
value ?: return
var curValue: Long = value
// we have at most 64 information bits. With overhead of 1 bit per 7 bits we need at most 10 bytes for encoding
val res = ByteArray(10)
var resSize = 0
while(curValue != 0L) {
// encode current 7 bits
var curByte = (curValue and VARINT_INFO_BITS_MASK.toLong())
// discard encoded bits. Note that unsigned shift is needed for cases with negative numbers
curValue = curValue ushr VARINT_INFO_BITS_COUNT
// check if there will be next byte and set util bit if needed
if (curValue != 0L) {
curByte = curByte or VARINT_UTIL_BIT_MASK.toLong()
}
res[resSize] = curByte.toByte()
resSize++
}
output.write(res, 0, resSize)
}
}
@@ -0,0 +1,182 @@
class DirectionRequest private constructor (command: Command = DirectionRequest.Command.fromIntToCommand(0)) {
var command : Command
private set
init {
this.command = command
}
enum class Command(val ord: Int) {
stop (0),
forward (1),
backward (2),
left (3),
right (4);
companion object {
fun fromIntToCommand (ord: Int): Command {
return when (ord) {
0 -> Command.stop
1 -> Command.forward
2 -> Command.backward
3 -> Command.left
4 -> Command.right
else -> throw InvalidProtocolBufferException("Error: got unexpected int ${ord} while parsing Command ");
}
}
}
}
fun writeTo (output: CodedOutputStream): Unit {
output.writeEnum (1, command.ord)
}
class BuilderDirectionRequest constructor (command: Command = DirectionRequest.Command.fromIntToCommand(0)) {
var command : Command
private set
fun setCommand(value: Command): DirectionRequest.BuilderDirectionRequest {
command = value
return this
}
init {
this.command = command
}
fun readFrom (input: CodedInputStream): DirectionRequest.BuilderDirectionRequest {
command = Command.fromIntToCommand(input.readEnum(1))
return this
}
fun build(): DirectionRequest {
return DirectionRequest(command)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
if (input.isAtEnd()) { return false }
val tag = input.readInt32NoTag()
if (tag == 0) { return false }
val fieldNumber = WireFormat.getTagFieldNumber(tag)
val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) {
1 -> command = Command.fromIntToCommand(input.readEnumNoTag())
}
return true}
fun parseFrom(input: CodedInputStream): DirectionRequest.BuilderDirectionRequest {
while(parseFieldFrom(input)) {}
return this
}
fun getSize(): Int {
var size = 0
size += WireFormat.getEnumSize(1, command.ord)
return size
}
}
fun mergeWith (other: DirectionRequest) {
command = other.command
}
fun mergeFrom (input: CodedInputStream) {
val builder = DirectionRequest.BuilderDirectionRequest()
mergeWith(builder.parseFrom(input).build())}
fun getSize(): Int {
var size = 0
size += WireFormat.getEnumSize(1, command.ord)
return size
}
}
class DirectionResponse private constructor (code: Int = 0, errorMsg: kotlin.String = "") {
var code : Int
private set
var errorMsg : kotlin.String
private set
init {
this.code = code
this.errorMsg = errorMsg
}
fun writeTo (output: CodedOutputStream): Unit {
output.writeInt32 (1, code)
output.writeString (2, errorMsg)
}
class BuilderDirectionResponse constructor (code: Int = 0, errorMsg: kotlin.String = "") {
var code : Int
private set
fun setCode(value: Int): DirectionResponse.BuilderDirectionResponse {
code = value
return this
}
var errorMsg : kotlin.String
private set
fun setErrorMsg(value: kotlin.String): DirectionResponse.BuilderDirectionResponse {
errorMsg = value
return this
}
init {
this.code = code
this.errorMsg = errorMsg
}
fun readFrom (input: CodedInputStream): DirectionResponse.BuilderDirectionResponse {
code = input.readInt32(1)
errorMsg = input.readString(2)
return this
}
fun build(): DirectionResponse {
return DirectionResponse(code, errorMsg)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
if (input.isAtEnd()) { return false }
val tag = input.readInt32NoTag()
if (tag == 0) { return false }
val fieldNumber = WireFormat.getTagFieldNumber(tag)
val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) {
1 -> code = input.readInt32NoTag()
2 -> errorMsg = input.readStringNoTag()
}
return true}
fun parseFrom(input: CodedInputStream): DirectionResponse.BuilderDirectionResponse {
while(parseFieldFrom(input)) {}
return this
}
fun getSize(): Int {
var size = 0
size += WireFormat.getInt32Size(1, code)
size += WireFormat.getStringSize(2, errorMsg)
return size
}
}
fun mergeWith (other: DirectionResponse) {
code = other.code
errorMsg = other.errorMsg
}
fun mergeFrom (input: CodedInputStream) {
val builder = DirectionResponse.BuilderDirectionResponse()
mergeWith(builder.parseFrom(input).build())}
fun getSize(): Int {
var size = 0
size += WireFormat.getInt32Size(1, code)
size += WireFormat.getStringSize(2, errorMsg)
return size
}
}
@@ -0,0 +1,10 @@
/**
* Created by user on 7/7/16.
*/
class InvalidProtocolBufferException(
override val message: String? = null,
override val cause : Throwable? = null
) : Throwable(message, cause) {
}
+24 -5
View File
@@ -1,4 +1,6 @@
import DirectionRequest.Command
import client.Client
import client.ClientHandler
import com.martiansoftware.jsap.FlaggedOption
import com.martiansoftware.jsap.JSAP
@@ -6,7 +8,13 @@ import com.martiansoftware.jsap.JSAP
* Created by user on 7/14/16.
*/
val correctDirectionValues: Array<Char> = arrayOf('w', 's', 'a', 'd', 'h');
//available direction symbols and command for symbol
val correctDirectionMap = mapOf<Char, Command>(
Pair('w', Command.forward),
Pair('s', Command.backward),
Pair('a', Command.left),
Pair('d', Command.right),
Pair('w', Command.stop))
fun main(args: Array<String>) {
val jsap: JSAP = JSAP()
@@ -24,8 +32,9 @@ fun main(args: Array<String>) {
val carControl = CarControl(client)
if (direction.equals('t', true)) {
initTextInterface(carControl)
} else if (correctDirectionValues.contains(direction)) {
carControl.executeCommand(direction)
} else if (correctDirectionMap.containsKey(direction)) {
carControl.executeCommand(correctDirectionMap.get(direction)!!)
printRequestResult()
} else {
println("incorrect direction.")
println(jsap.getHelp())
@@ -48,16 +57,26 @@ fun initTextInterface(carControl: CarControl) {
println(helpMessage)
} else {
val directionChar = nextLine.get(0)
if (!correctDirectionValues.contains(directionChar)) {
if (!correctDirectionMap.containsKey(directionChar)) {
println("incorrect argument \"$nextLine\"")
println(helpMessage)
} else {
carControl.executeCommand(directionChar)
carControl.executeCommand(correctDirectionMap.get(directionChar)!!)
printRequestResult()
}
}
}
}
fun printRequestResult() {
synchronized(ClientHandler.requestResult, {
if (ClientHandler.requestResult.code != 0) {
println("result code: ${ClientHandler.requestResult.code}\nerror message ${ClientHandler.requestResult.errorString}")
}
})
}
fun setOptions(jsap: JSAP) {
val opthost = FlaggedOption("host").setStringParser(JSAP.STRING_PARSER).setRequired(true).setShortFlag('h').setLongFlag("host")
val optPort = FlaggedOption("port").setStringParser(JSAP.INTEGER_PARSER).setDefault("8888").setRequired(false).setShortFlag('p').setLongFlag("port")
@@ -0,0 +1,114 @@
/**
* Created by user on 7/6/16.
*/
object WireFormat {
// couple of constants for magic numbers
val TAG_TYPE_BITS: Int = 3
val TAG_TYPE_MASK: Int = (1 shl TAG_TYPE_BITS) - 1
val VARINT_INFO_BITS_COUNT: Int = 7
val VARINT_INFO_BITS_MASK: Int = 0b01111111 // mask for separating lowest 7 bits, where actual information stored
val VARINT_UTIL_BIT_MASK: Int = 0b10000000 // mask for separating highest bit, that indicates next byte presence
val FIXED_32_BYTE_SIZE: Int = 4
val FIXED_64_BYTE_SIZE: Int = 8
fun getTagWireType(tag: Int): WireType {
return WireType from (tag and TAG_TYPE_MASK).toByte()
}
fun getTagFieldNumber(tag: Int): Int {
return tag ushr TAG_TYPE_BITS
}
// TODO: refactor casts into function overloading as soon as translator will support it
fun getTagSize(fieldNumber: Int, wireType: WireType): Int {
return getVarint32Size((fieldNumber shl 3) or wireType.ordinal)
}
fun getVarint32Size(value: Int): Int {
var curValue = value
var size = 0
while (curValue != 0) {
size += 1
curValue = value ushr VARINT_INFO_BITS_COUNT
}
return size
}
fun getVarint64Size(value: Long): Int {
var curValue = value
var size = 0
while (curValue != 0L) {
size += 1
curValue = value ushr VARINT_INFO_BITS_COUNT
}
return size
}
fun getZigZag32Size(value: Int): Int {
return getVarint32Size((value shl 1) xor (value shr 31))
}
fun getZigZag64Size(value: Long): Int {
return getVarint64Size((value shl 1) xor (value shr 63))
}
fun getInt32Size(fieldNumber: Int, value: Int): Int {
return getTagSize(fieldNumber, WireType.VARINT) + getVarint32Size(value)
}
fun getUInt32Size(fieldNumber: Int, value: Int): Int {
return getInt32Size(fieldNumber, value)
}
fun getInt64Size(fieldNumber: Int, value: Long): Int {
return getTagSize(fieldNumber, WireType.VARINT) + getVarint64Size(value)
}
fun getUInt64Size(fieldNumber: Int, value: Long): Int {
return getInt64Size(fieldNumber, value)
}
fun getBoolSize(fieldNumber: Int, value: Boolean): Int {
val intValue = if (value) 1 else 0
return getInt32Size(fieldNumber, intValue)
}
fun getEnumSize(fieldNumber: Int, value: Int): Int {
return getInt32Size(fieldNumber, value)
}
fun getSInt32Size(fieldNumber: Int, value: Int): Int {
return getTagSize(fieldNumber, WireType.VARINT) + getZigZag32Size(value)
}
fun getSInt64Size(fieldNumber: Int, value: Long): Int {
return getTagSize(fieldNumber, WireType.VARINT) + getZigZag64Size(value)
}
fun getFixed32Size(fieldNumber: Int, value: Long): Int {
return getTagSize(fieldNumber, WireType.FIX_32) + FIXED_32_BYTE_SIZE
}
fun getFixed64Size(fieldNumber: Int, value: Long): Int {
return getTagSize(fieldNumber, WireType.FIX_64) + FIXED_64_BYTE_SIZE
}
fun getDoubleSize(fieldNumber: Int, value: Double): Int {
return getTagSize(fieldNumber, WireType.FIX_32) + FIXED_32_BYTE_SIZE
}
fun getFloatSize(fieldNumber: Int, value: Float): Int {
return getTagSize(fieldNumber, WireType.FIX_64) + FIXED_64_BYTE_SIZE
}
fun getStringSize(fieldNumber: Int, value: String): Int {
val encodedStringSize = value.toByteArray(Charsets.UTF_8).size //TODO: not sure if it's the best way to do it
return encodedStringSize + getTagSize(fieldNumber, WireType.LENGTH_DELIMITED) + getVarint32Size(encodedStringSize)
}
fun getBytesSize(fieldNumber: Int, value: ByteArray): Int {
return value.size + getTagSize(fieldNumber, WireType.LENGTH_DELIMITED) + getVarint32Size(value.size)
}
}
@@ -0,0 +1,28 @@
/**
* Created by Dmitry Savvinov on 7/6/16.
* Enum for possible WireTypes.
* See details at [official Google reference]()https://developers.google.com/protocol-buffers/docs/encoding#structure)
*/
enum class WireType(val id: Int) {
VARINT(0), // int32, int64, uint32, uint64, sint32, sint64, bool, enum
FIX_64(1), // fixed64, sfixed64, double
LENGTH_DELIMITED(2), // string, bytes, embedded messages, packed repeated fields
START_GROUP(3), // groups (deprecated)
END_GROUP(4), // groups (deprecated)
FIX_32(5); // fixed32, sfixed32, float
companion object {
infix fun from (value: Byte): WireType {
return when (value) {
0.toByte() -> VARINT
1.toByte() -> FIX_64
2.toByte() -> LENGTH_DELIMITED
3.toByte() -> START_GROUP
4.toByte() -> END_GROUP
5.toByte() -> FIX_32
else -> throw IllegalArgumentException()
}
}
}
}
@@ -19,7 +19,7 @@ class Client constructor(host: String, port: Int) {
this.port = port
}
fun sendRequest(request: HttpRequest): Int {
fun sendRequest(request: HttpRequest) {
val group = NioEventLoopGroup(1)
try {
val bootstrap = Bootstrap()
@@ -29,15 +29,14 @@ class Client constructor(host: String, port: Int) {
channel.writeAndFlush(request)
channel.closeFuture().sync()
} catch (e: InterruptedException) {
println("interrupted before request done")
return 2
ClientHandler.requestResult.code = 2
ClientHandler.requestResult.errorString = "command execution interrupted"
} catch (e: ConnectException) {
println("connection error")
return 1
ClientHandler.requestResult.code = 1
ClientHandler.requestResult.errorString = "don't can connect to server ($host:$port)"
} finally {
group.shutdownGracefully()
}
return ClientHandler.requestResult.code
}
}
@@ -1,9 +1,12 @@
package client
import CodedInputStream
import DirectionResponse
import InvalidProtocolBufferException
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.SimpleChannelInboundHandler
import io.netty.handler.codec.http.DefaultHttpContent
import io.netty.handler.codec.http.HttpContent
import java.io.ByteArrayInputStream
/**
* Created by user on 7/8/16.
@@ -11,7 +14,8 @@ import io.netty.handler.codec.http.HttpContent
class ClientHandler : SimpleChannelInboundHandler<Any> {
object requestResult {
var code: Int = -1
var code = -1
var errorString = ""
}
constructor()
@@ -21,18 +25,31 @@ class ClientHandler : SimpleChannelInboundHandler<Any> {
override fun channelReadComplete(ctx: ChannelHandlerContext) {
if (contentBytes.size == 0) {
//socket is closed
ctx.close()
return
}
val resultCode: Int = contentBytes[0].toInt()
val responseStream = CodedInputStream(ByteArrayInputStream(contentBytes))
val response = DirectionResponse.BuilderDirectionResponse().build()
try {
response.mergeFrom(responseStream)
} catch (e: InvalidProtocolBufferException) {
synchronized(requestResult, {
requestResult.code = 1
requestResult.errorString = "protobuf parsing error. bytes from server is not message. stack trace:\n ${e.message}"
})
return
}
synchronized(requestResult, {
requestResult.code = resultCode
requestResult.code = response.code
requestResult.errorString = response.errorMsg
})
ctx.close()
}
override fun channelRead0(ctx: ChannelHandlerContext?, msg: Any?) {
if (msg is DefaultHttpContent) {
//read bytes from http body
val contentsBytes = msg.content();
contentBytes = ByteArray(contentsBytes.capacity())
contentsBytes.readBytes(contentBytes)
File diff suppressed because it is too large Load Diff