Added draft version of runtime classes for Kotlin Protobuf
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
Generated
+9
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Generated
+22
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<resourceExtensions />
|
||||||
|
<wildcardResourcePatterns>
|
||||||
|
<entry name="!?*.java" />
|
||||||
|
<entry name="!?*.form" />
|
||||||
|
<entry name="!?*.class" />
|
||||||
|
<entry name="!?*.groovy" />
|
||||||
|
<entry name="!?*.scala" />
|
||||||
|
<entry name="!?*.flex" />
|
||||||
|
<entry name="!?*.kt" />
|
||||||
|
<entry name="!?*.clj" />
|
||||||
|
<entry name="!?*.aj" />
|
||||||
|
</wildcardResourcePatterns>
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile default="true" name="Default" enabled="false">
|
||||||
|
<processorPath useClasspath="true" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<settings default="" />
|
||||||
|
</component>
|
||||||
Generated
+21
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="MavenImportPreferences">
|
||||||
|
<option name="generalSettings">
|
||||||
|
<MavenGeneralSettings>
|
||||||
|
<option name="mavenHome" value="Bundled (Maven 3)" />
|
||||||
|
</MavenGeneralSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
|
||||||
|
<OptionsSetting value="true" id="Add" />
|
||||||
|
<OptionsSetting value="true" id="Remove" />
|
||||||
|
<OptionsSetting value="true" id="Checkout" />
|
||||||
|
<OptionsSetting value="true" id="Update" />
|
||||||
|
<OptionsSetting value="true" id="Status" />
|
||||||
|
<OptionsSetting value="true" id="Edit" />
|
||||||
|
<ConfirmationsSetting value="0" id="Add" />
|
||||||
|
<ConfirmationsSetting value="0" id="Remove" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_3" default="true" assert-keyword="false" jdk-15="false" />
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/compiler.iml" filepath="$PROJECT_DIR$/.idea/compiler.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/test" type="java-resource" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
@@ -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<ValueType> (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<Int> {
|
||||||
|
val (fieldNumber, wireType) = readAndParseTag()
|
||||||
|
if (wireType != WireType.VARINT)
|
||||||
|
throw InvalidProtocolBufferException("Expected Varint tag, got ${wireType.name}")
|
||||||
|
val value = readRawVarint32()
|
||||||
|
return Field<Int>(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<Int> {
|
||||||
|
return readInt32()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readInt64(): Field<Long> {
|
||||||
|
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<Long> {
|
||||||
|
return readUInt64()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readBool(): Field<Boolean> {
|
||||||
|
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<Int> {
|
||||||
|
return readInt32()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readSInt32(): Field<Int> {
|
||||||
|
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<Long> {
|
||||||
|
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<Int> {
|
||||||
|
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<Int> {
|
||||||
|
return readFixed32()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readFixed64(): Field<Long> {
|
||||||
|
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<Long> {
|
||||||
|
return readFixed64()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readDouble(): Field<Double> {
|
||||||
|
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<Float> {
|
||||||
|
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<String> {
|
||||||
|
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<Int, WireType> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
testMessageSerialization()
|
||||||
|
println("OK")
|
||||||
|
}
|
||||||
@@ -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 <T> assertSuccessfulRead(fieldNumber: Int, value: T, field: CodedInputStream.Field<T>) {
|
||||||
|
assert(field.fieldNumber == fieldNumber)
|
||||||
|
assert(field.wireType == WireType.VARINT)
|
||||||
|
assert(field.value == value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
testTrivialPositiveVarInts()
|
||||||
|
testMinPossibleVarInts()
|
||||||
|
testMaxPossibleVarInts()
|
||||||
|
testMaxPossibleFieldNumber()
|
||||||
|
testZigZag32()
|
||||||
|
testZigZag64()
|
||||||
|
testBoolean()
|
||||||
|
testEnum()
|
||||||
|
testFloatAndDouble()
|
||||||
|
testStrings()
|
||||||
|
print("OK")
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user