diff --git a/proto/compiler/.gitignore b/proto/compiler/.gitignore new file mode 100644 index 00000000000..5b4bbf65181 --- /dev/null +++ b/proto/compiler/.gitignore @@ -0,0 +1,21 @@ +.DS_Store +.idea/shelf +/dependencies +/dist +out +tmp +workspace.xml +*.versionsBackup + +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + diff --git a/proto/compiler/.idea/compiler.iml b/proto/compiler/.idea/compiler.iml new file mode 100644 index 00000000000..d6ebd480598 --- /dev/null +++ b/proto/compiler/.idea/compiler.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/proto/compiler/.idea/compiler.xml b/proto/compiler/.idea/compiler.xml new file mode 100644 index 00000000000..96cc43efa6a --- /dev/null +++ b/proto/compiler/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/proto/compiler/.idea/copyright/profiles_settings.xml b/proto/compiler/.idea/copyright/profiles_settings.xml new file mode 100644 index 00000000000..e7bedf3377d --- /dev/null +++ b/proto/compiler/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/proto/compiler/.idea/misc.xml b/proto/compiler/.idea/misc.xml new file mode 100644 index 00000000000..880e17b132c --- /dev/null +++ b/proto/compiler/.idea/misc.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/proto/compiler/.idea/modules.xml b/proto/compiler/.idea/modules.xml new file mode 100644 index 00000000000..b67420d2e33 --- /dev/null +++ b/proto/compiler/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/proto/compiler/lib/kotlin-reflect.jar b/proto/compiler/lib/kotlin-reflect.jar new file mode 100644 index 00000000000..62340e2dae4 Binary files /dev/null and b/proto/compiler/lib/kotlin-reflect.jar differ diff --git a/proto/compiler/lib/kotlin-runtime-sources.jar b/proto/compiler/lib/kotlin-runtime-sources.jar new file mode 100644 index 00000000000..e661b30034a Binary files /dev/null and b/proto/compiler/lib/kotlin-runtime-sources.jar differ diff --git a/proto/compiler/lib/kotlin-runtime.jar b/proto/compiler/lib/kotlin-runtime.jar new file mode 100644 index 00000000000..4d7a92703f8 Binary files /dev/null and b/proto/compiler/lib/kotlin-runtime.jar differ diff --git a/proto/compiler/protobuf_draft.iml b/proto/compiler/protobuf_draft.iml new file mode 100644 index 00000000000..7fa3b1b2717 --- /dev/null +++ b/proto/compiler/protobuf_draft.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/proto/compiler/src/CodedInputStream.kt b/proto/compiler/src/CodedInputStream.kt new file mode 100644 index 00000000000..a63b8f5012f --- /dev/null +++ b/proto/compiler/src/CodedInputStream.kt @@ -0,0 +1,234 @@ +import java.nio.ByteBuffer +import java.nio.ByteOrder + +/** + * 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: decide, if we want to optimize for performance (then we have to deal with spaghetti-code) +class CodedInputStream(input: java.io.InputStream) { + + data class Field (val fieldNumber: Int, val wireType: WireType, val value: ValueType) // TODO: think about variance + + val bufferedInput: java.io.BufferedInputStream + init { + bufferedInput = java.io.BufferedInputStream(input) // TODO: Java's realization uses hand-written buffers. Why? + } + + fun readInt32(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.VARINT) + throw InvalidProtocolBufferException("Expected Varint tag, got ${wireType.name}") + val value = readRawVarint32() + return Field(fieldNumber, wireType, 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, reading unsigned ints simply redirect call to corresponding signed-reading method + fun readUInt32(): Field { + return readInt32() + } + + fun readInt64(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.VARINT) + throw InvalidProtocolBufferException("Expected Varint tag, got ${wireType.name}") + val value = readRawVarint64() + return Field(fieldNumber, wireType, value) + } + + // See note on unsigned integers implementations above + fun readUInt64(): Field { + return readUInt64() + } + + fun readBool(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.VARINT) + throw InvalidProtocolBufferException("Expected Varint tag, got ${wireType.name}") + + val readValue = readRawVarint32() + val boolValue = when (readValue) { + 0 -> false + 1 -> true + else -> throw InvalidProtocolBufferException("Expected boolean-encoding (1 or 0), got $readValue") + } + + return Field(fieldNumber, wireType, boolValue) + } + + // Reading enums is like reading one int32 number. Caller is responsible for converting this ordinal to enum-object + fun readEnum(): Field { + return readInt32() + } + + fun readSInt32(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.VARINT) + throw InvalidProtocolBufferException("Expected Varint tag, got ${wireType.name}") + val value = readZigZag32() + return Field(fieldNumber, wireType, value) + } + + fun readSInt64(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.VARINT) + throw InvalidProtocolBufferException("Expected Varint tag, got ${wireType.name}") + val value = readZigZag64() + return Field(fieldNumber, wireType, value) + } + + fun readFixed32(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.FIX_32) + throw InvalidProtocolBufferException("Expected FIX_32 tag, got ${wireType.name}") + val value = readLittleEndianInt() + return Field(fieldNumber, wireType, value) + } + + fun readSFixed32(): Field { + return readFixed32() + } + + fun readFixed64(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.FIX_64) + throw InvalidProtocolBufferException("Expected FIX_64 tag, got ${wireType.name}") + val value = readLittleEndianLong() + return Field(fieldNumber, wireType, value) + } + + fun readSFixed64(): Field { + return readFixed64() + } + + fun readDouble(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.FIX_64) + throw InvalidProtocolBufferException("Expected FIX_64 tag, got ${wireType.name}") + val value = readLittleEndianDouble() + return Field(fieldNumber, wireType, value) + } + + fun readFloat(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.FIX_32) + throw InvalidProtocolBufferException("Expected FIX_64 tag, got ${wireType.name}") + val value = readLittleEndianFloat() + return Field(fieldNumber, wireType, value) + } + + fun readString(): Field { + val (fieldNumber, wireType) = readAndParseTag() + if (wireType != WireType.LENGTH_DELIMITED) + throw InvalidProtocolBufferException("Expected LENGTH_DELMITED tag, got ${wireType.name}") + val length = readRawVarint32() + val value = String(readRawBytes(length)) + return Field(fieldNumber, wireType, value) + } + + // ============ private methods ================== + + private fun readLittleEndianDouble(): Double { + val byteBuffer = ByteBuffer.wrap(readRawBytes(8)) + byteBuffer.order(ByteOrder.LITTLE_ENDIAN) + return byteBuffer.getDouble(0) + } + + private fun readLittleEndianFloat(): Float { + val byteBuffer = ByteBuffer.wrap(readRawBytes(4)) + byteBuffer.order(ByteOrder.LITTLE_ENDIAN) + return byteBuffer.getFloat(0) + } + + private fun readLittleEndianInt(): Int { + val byteBuffer = ByteBuffer.wrap(readRawBytes(8)) + byteBuffer.order(ByteOrder.LITTLE_ENDIAN) + return byteBuffer.getInt(0) + } + + private fun readLittleEndianLong(): Long { + val byteBuffer = ByteBuffer.wrap(readRawBytes(4)) + byteBuffer.order(ByteOrder.LITTLE_ENDIAN) + return byteBuffer.getLong(0) + } + + private fun readRawBytes(count: Int): ByteArray { + val ba = ByteArray(count) + for (i in 0..(count - 1)) { + ba[i] = bufferedInput.read().toByte() + } + return ba + } + + private fun readTag(): Int { + if (isAtEnd()) { + return 0 // we can safely return 0 as sign of end of message, because 0-tags are illegal + } + val tag = readRawVarint32() + if (tag == 0) { // if we somehow read 0-tag, then message is corrupted + throw InvalidProtocolBufferException("Invalid tag 0") + } + return tag + } + + private fun readAndParseTag(): Pair { + val tag = readTag() + return Pair(WireFormat.getTagFieldNumber(tag), WireFormat.getTagWireType(tag)) + } + + private fun readRawVarint32(): 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 127) shl (7 * step)) + step++ + if ((byte and 128) == 0) { + done = true + } + } + return result + } + + private fun readRawVarint64(): 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 127).toLong() shl (7 * step)) + step++ + if ((byte and 128) == 0 || byte == -1) { + done = true + } + } + return result + } + + private fun readZigZag32(): Int { + val value = readRawVarint32() + return (value shr 1) xor (-(value and 1)) + } + + private fun readZigZag64(): Long { + val value = readRawVarint64() + return (value shr 1) xor (-(value and 1L)) + } + + private fun isAtEnd(): Boolean { + bufferedInput.mark(1) + val byte = bufferedInput.read() + bufferedInput.reset() + return byte == -1 + } +} + diff --git a/proto/compiler/src/CodedOutputStream.kt b/proto/compiler/src/CodedOutputStream.kt new file mode 100644 index 00000000000..04a1fdd3bcd --- /dev/null +++ b/proto/compiler/src/CodedOutputStream.kt @@ -0,0 +1,149 @@ +import java.nio.ByteBuffer +import java.nio.ByteOrder + +/** + * 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 + writeVarint32(tag) + } + + fun writeInt32(fieldNumber: Int, value: Int) { + writeTag(fieldNumber, WireType.VARINT) + writeVarint32(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) { + writeInt32(fieldNumber, value) + } + + fun writeInt64(fieldNumber: Int, value: Long) { + writeTag(fieldNumber, WireType.VARINT) + writeVarint64(value) + } + + // See notes on unsigned integers implementation above + fun writeUIn64(fieldNumber: Int, value: Long) { + writeInt64(fieldNumber, value) + } + + fun writeBool(fieldNumber: Int, value: Boolean) { + writeInt32(fieldNumber, 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) { + writeInt32(fieldNumber, value) + } + + fun writeSInt32(fieldNumber: Int, value: Int) { + writeInt32(fieldNumber, (value shl 1) xor (value shr 31)) + } + + fun writeSInt64(fieldNumber: Int, value: Long) { + writeInt64(fieldNumber, (value shl 1) xor (value shr 31)) + } + + fun writeFixed32(fieldNumber: Int, value: Int) { + writeTag(fieldNumber, WireType.FIX_32) + writeLittleEndian(value) + } + + // See notes on unsigned integers implementation above + fun writeSFixed32(fieldNumber: Int, value: Int) { + writeFixed32(fieldNumber, value) + } + + fun writeFixed64(fieldNumber: Int, value: Long) { + writeTag(fieldNumber, WireType.FIX_64) + writeLittleEndian(value) + } + + // See notes on unsigned integers implementation above + fun writeSFixed64(fieldNumber: Int, value: Long) { + writeFixed64(fieldNumber, value) + } + + fun writeDouble(fieldNumber: Int, value: Double) { + writeTag(fieldNumber, WireType.FIX_64) + writeLittleEndian(value) + } + + fun writeFloat(fieldNumber: Int, value: Float) { + writeTag(fieldNumber, WireType.FIX_32) + writeLittleEndian(value) + } + + fun writeString(fieldNumber: Int, value: String) { + writeTag(fieldNumber, WireType.LENGTH_DELIMITED) + writeVarint32(value.length) + output.write(value.toByteArray()) + } + + fun writeLittleEndian(value: Int) { + val bb = ByteBuffer.allocate(4) + bb.order(ByteOrder.LITTLE_ENDIAN) + bb.putInt(value) + output.write(bb.array()) + } + + fun writeLittleEndian(value: Long) { + val bb = ByteBuffer.allocate(8) + bb.order(ByteOrder.LITTLE_ENDIAN) + bb.putLong(value) + output.write(bb.array()) + } + + fun writeLittleEndian(value: Double) { + val bb = ByteBuffer.allocate(8) + bb.order(ByteOrder.LITTLE_ENDIAN) + bb.putDouble(value) + output.write(bb.array()) + } + + fun writeLittleEndian(value: Float) { + val bb = ByteBuffer.allocate(4) + bb.order(ByteOrder.LITTLE_ENDIAN) + bb.putFloat(value) + output.write(bb.array()) + } + + fun writeVarint32(value: Int) { + var curValue = value + val res = ByteArray(5) + var resSize = 0 + do { + var curByte = (curValue and 127) + curValue = curValue ushr 7 + if (curValue != 0) { + curByte = curByte or 128 + } + res[resSize] = curByte.toByte() + resSize++ + } while(curValue != 0) + output.write(res, 0, resSize) + } + + fun writeVarint64(value: Long) { + var curValue = value + val res = ByteArray(10) // we reserve 10 bytes for the cases when value was negative int32/int64 + var resSize = 0 + while(curValue != 0L) { + var curByte = (curValue and 127L) + curValue = curValue ushr 7 + if (curValue != 0L) { + curByte = curByte or 128L + } + + res[resSize] = curByte.toByte() + resSize++ + } + output.write(res, 0, resSize) + } +} diff --git a/proto/compiler/src/InvalidProtocolBufferException.kt b/proto/compiler/src/InvalidProtocolBufferException.kt new file mode 100644 index 00000000000..91f59d8a91e --- /dev/null +++ b/proto/compiler/src/InvalidProtocolBufferException.kt @@ -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) { + +} \ No newline at end of file diff --git a/proto/compiler/src/Message.kt b/proto/compiler/src/Message.kt new file mode 100644 index 00000000000..0cb812de2db --- /dev/null +++ b/proto/compiler/src/Message.kt @@ -0,0 +1,15 @@ +/** + * Created by user on 7/8/16. + */ + +interface Message { + fun writeTo(output: CodedOutputStream) + fun readFrom(input: CodedInputStream) : Message + fun getBuilder() : Builder + + //TODO: think about something similar to static method getDefaultInstance() + + interface Builder { + fun build(): Message + } +} diff --git a/proto/compiler/src/PersonMessage.kt b/proto/compiler/src/PersonMessage.kt new file mode 100644 index 00000000000..01fd498807c --- /dev/null +++ b/proto/compiler/src/PersonMessage.kt @@ -0,0 +1,48 @@ +/** + * Created by user on 7/8/16. + */ + +// We don't use Message interface for a moment because of LLVM-translator restrictions +class PersonMessage(val name: String, val id: Long, val hasCat: Boolean) // : Message +{ + fun writeTo(output: CodedOutputStream) { + output.writeString(1, name) + output.writeInt64(2, id) + output.writeBool(3, hasCat) + } + + fun readFrom(input: CodedInputStream) : PersonMessage { + val newName = input.readString().value + val newId = input.readInt64().value + val newHasCatFlag = input.readBool().value + return PersonMessage(newName, newId, newHasCatFlag) + } + + fun getBuilder(): PersonBuilder { + return PersonBuilder() + } + + // No interface, see above + class PersonBuilder { // : Message.Builder + // TODO: think how can we + var name_: String = "" + var id_: Long = 0 + var hasCat_: Boolean = false + + fun setName(name: String) { + name_ = name + } + + fun setId(id: Long) { + id_ = id + } + + fun setHasCat(hasCat: Boolean) { + hasCat_ = hasCat + } + + fun build(): PersonMessage { + return PersonMessage(name_, id_, hasCat_) // Java implementation caches such instances + } + } +} \ No newline at end of file diff --git a/proto/compiler/src/TestMessages.kt b/proto/compiler/src/TestMessages.kt new file mode 100644 index 00000000000..d148ece15a1 --- /dev/null +++ b/proto/compiler/src/TestMessages.kt @@ -0,0 +1,24 @@ +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream + +/** + * Created by user on 7/8/16. + */ + +fun testMessageSerialization() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + val msg = PersonMessage(name = "John Doe", id = 42, hasCat = true) + msg.writeTo(outs) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + val readMsg = PersonMessage("", 0, false) + readMsg.readFrom(ins) + + assert(msg == readMsg) +} + +fun main(args: Array) { + testMessageSerialization() + println("OK") +} diff --git a/proto/compiler/src/TestStreams.kt b/proto/compiler/src/TestStreams.kt new file mode 100644 index 00000000000..d0b315fe85d --- /dev/null +++ b/proto/compiler/src/TestStreams.kt @@ -0,0 +1,159 @@ +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream + +/** + * Created by user on 7/7/16. + */ + +fun testTrivialPositiveVarInts() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeInt32(1, 42) + outs.writeInt32(16, 1) + outs.writeInt32(2, -2) + outs.writeInt32(3, -21321) + outs.writeInt64(5, 42L) + outs.writeInt64(6, 1232132131212321L) + outs.writeInt64(7, -2) + outs.writeInt64(8, -21321L) + outs.writeInt64(9, -32132132132131L) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(1, 42, ins.readInt32()) + assertSuccessfulRead(42, 1, ins.readInt32()) + assertSuccessfulRead(2, -2, ins.readInt32()) + assertSuccessfulRead(3, -21321, ins.readInt32()) + assertSuccessfulRead(5, 42L, ins.readInt64()) + assertSuccessfulRead(6, 1232132131212321L, ins.readInt64()) + assertSuccessfulRead(7, -2, ins.readInt64()) + assertSuccessfulRead(8, -21321L, ins.readInt64()) + assertSuccessfulRead(9, -32132132132131L, ins.readInt64()) +} + + +fun testMinPossibleVarInts() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeInt32(1, Int.MIN_VALUE) + outs.writeInt64(2, Long.MIN_VALUE) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(1, Int.MIN_VALUE, ins.readInt32()) + assertSuccessfulRead(2, Long.MIN_VALUE, ins.readInt64()) +} + +fun testMaxPossibleVarInts() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeInt32(1, Int.MAX_VALUE) + outs.writeInt64(2, Long.MAX_VALUE) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(1, Int.MAX_VALUE, ins.readInt32()) + assertSuccessfulRead(2, Long.MAX_VALUE, ins.readInt64()) +} + +fun testMaxPossibleFieldNumber() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeInt64(536870911, Long.MAX_VALUE) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(536870911, Long.MAX_VALUE, ins.readInt64()) +} + +fun testZigZag32() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeSInt32(5, -213123) + outs.writeSInt32(1, 3123123) + outs.writeSInt32(4, -12345675) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(5, -213123, ins.readSInt32()) + assertSuccessfulRead(1, 3123123, ins.readSInt32()) + assertSuccessfulRead(4, -12345675, ins.readSInt32()) +} + +fun testZigZag64() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeSInt64(5, -213123L) + outs.writeSInt64(1, 3123123L) + outs.writeSInt64(4, -12345675L) + outs.writeSInt64(12, -123456789012L) + outs.writeSInt64(42, 483567314231L) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(5, -213123L, ins.readSInt64()) + assertSuccessfulRead(1, 3123123L, ins.readSInt64()) + assertSuccessfulRead(4, -12345675L, ins.readSInt64()) + assertSuccessfulRead(12, -123456789012L, ins.readSInt64()) + assertSuccessfulRead(42, 483567314231L, ins.readSInt64()) +} + +fun testBoolean() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeBool(1, true) + outs.writeBool(2, false) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(1, true, ins.readBool()) + assertSuccessfulRead(2, false, ins.readBool()) +} + +fun testEnum() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeEnum(1, WireType.END_GROUP.ordinal) + outs.writeEnum(2, WireType.FIX_64.ordinal) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(1, WireType.END_GROUP.ordinal, ins.readEnum()) + assertSuccessfulRead(2, WireType.FIX_64.ordinal, ins.readEnum()) +} + +fun testFloatAndDouble() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeDouble(42, 123212.34282) + outs.writeDouble(15, Math.PI) + outs.writeFloat(14, -1.32131321f) + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(42, 123212.34282, ins.readDouble()) + assertSuccessfulRead(15, Math.PI, ins.readDouble()) + assertSuccessfulRead(14, -1.32131321f, ins.readFloat()) +} + +fun testStrings() { + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + outs.writeString(42, "dasddasd asd ") + outs.writeString(15, """!@#$%^&*()QWERTYUI выфвфывфы""") + + val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + assertSuccessfulRead(42, "dasddasd asd ", ins.readString()) + assertSuccessfulRead(15, """!@#$%^&*()QWERTYUI выфвфывфы""", ins.readString()) +} + +fun assertSuccessfulRead(fieldNumber: Int, value: T, field: CodedInputStream.Field) { + assert(field.fieldNumber == fieldNumber) + assert(field.wireType == WireType.VARINT) + assert(field.value == value) +} + +fun main(args: Array) { + testTrivialPositiveVarInts() + testMinPossibleVarInts() + testMaxPossibleVarInts() + testMaxPossibleFieldNumber() + testZigZag32() + testZigZag64() + testBoolean() + testEnum() + testFloatAndDouble() + testStrings() + print("OK") +} \ No newline at end of file diff --git a/proto/compiler/src/WireFormat.kt b/proto/compiler/src/WireFormat.kt new file mode 100644 index 00000000000..ebe56175fee --- /dev/null +++ b/proto/compiler/src/WireFormat.kt @@ -0,0 +1,17 @@ +/** + * Created by user on 7/6/16. + */ + + +object WireFormat { + val TAG_TYPE_BITS: Int = 3 + val TAG_TYPE_MASK: Int = (1 shl TAG_TYPE_BITS) - 1 + + 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 + } +} \ No newline at end of file diff --git a/proto/compiler/src/WireType.kt b/proto/compiler/src/WireType.kt new file mode 100644 index 00000000000..6cc55da3f42 --- /dev/null +++ b/proto/compiler/src/WireType.kt @@ -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() + } + } + } +} \ No newline at end of file