mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-28 12:56:49 +01:00
Utils: added simple Protobuf creator
This commit is contained in:
parent
8c69a8a007
commit
858c53efb8
@ -0,0 +1,24 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util.protobuf;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
public class ProtobufUtils {
|
||||
public static byte[] encode_varint(int value){
|
||||
if(value == 0){
|
||||
return new byte[]{0x00};
|
||||
}
|
||||
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(4);
|
||||
|
||||
while(value > 0){
|
||||
byte newByte = (byte)(value & 0b01111111);
|
||||
value >>= 7;
|
||||
if(value != 0){
|
||||
newByte |= 0b10000000;
|
||||
}
|
||||
bytes.write(newByte);
|
||||
}
|
||||
|
||||
return bytes.toByteArray();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BytesMessageField extends MessageField{
|
||||
private byte[] fieldValueBytes;
|
||||
|
||||
protected BytesMessageField(int fieldNumber, byte[] value) {
|
||||
super(fieldNumber, FieldType.LENGTH_DELIMITED);
|
||||
fieldValueBytes = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteArrayOutputStream os) throws IOException {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields;
|
||||
|
||||
public class Int32MessageField extends VarintMessageField{
|
||||
public Int32MessageField(int fieldNumber, int value){
|
||||
super(fieldNumber, value);
|
||||
this.fieldType = FieldType.INT_32_BIT;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields;
|
||||
|
||||
public class Int64MessageField extends VarintMessageField{
|
||||
public Int64MessageField(int fieldNumber, int value){
|
||||
super(fieldNumber, value);
|
||||
this.fieldType = FieldType.INT_64_BIT;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public abstract class MessageField {
|
||||
public static enum FieldType{
|
||||
VARINT,
|
||||
INT_64_BIT,
|
||||
LENGTH_DELIMITED,
|
||||
START_GROUP,
|
||||
END_GROUP,
|
||||
INT_32_BIT
|
||||
}
|
||||
|
||||
int fieldNumber;
|
||||
FieldType fieldType;
|
||||
|
||||
protected MessageField(int fieldNumber, FieldType fieldType){
|
||||
this.fieldNumber = fieldNumber;
|
||||
this.fieldType = fieldType;
|
||||
}
|
||||
|
||||
protected byte[] getStartBytes(){
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(4);
|
||||
|
||||
byte result = (byte) (fieldType.ordinal() & 0b111);
|
||||
|
||||
int fieldNumber = this.fieldNumber;
|
||||
|
||||
result |= (fieldNumber & 0b1111) << 3;
|
||||
fieldNumber >>= 4;
|
||||
if(fieldNumber > 0){
|
||||
result |= 0b10000000;
|
||||
}
|
||||
bytes.write(result);
|
||||
|
||||
while(fieldNumber > 0){
|
||||
byte newByte = (byte)(fieldNumber & 0b01111111);
|
||||
fieldNumber >>= 7;
|
||||
if(fieldNumber != 0){
|
||||
newByte |= 0b10000000;
|
||||
}
|
||||
bytes.write(newByte);
|
||||
}
|
||||
|
||||
return bytes.toByteArray();
|
||||
}
|
||||
|
||||
abstract public void encode(ByteArrayOutputStream os) throws IOException;
|
||||
|
||||
public byte[] encodeToBytes() throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
encode(os);
|
||||
return os.toByteArray();
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.protobuf.ProtobufUtils;
|
||||
|
||||
public class NestedMessageField extends MessageField{
|
||||
MessageField[] children;
|
||||
|
||||
public NestedMessageField(int fieldNumber, MessageField... children) {
|
||||
super(fieldNumber, FieldType.LENGTH_DELIMITED);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteArrayOutputStream os) throws IOException {
|
||||
ByteArrayOutputStream childStream = new ByteArrayOutputStream();
|
||||
for(MessageField child : children){
|
||||
child.encode(childStream);
|
||||
}
|
||||
|
||||
os.write(getStartBytes());
|
||||
os.write(ProtobufUtils.encode_varint(childStream.size()));
|
||||
os.write(childStream.toByteArray());
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields;
|
||||
|
||||
public class RootMessageField extends NestedMessageField{
|
||||
public RootMessageField(MessageField... children) {
|
||||
super(0, children);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getStartBytes() {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
public void encode(ByteArrayOutputStream os) throws IOException {
|
||||
ByteArrayOutputStream childrenOs = new ByteArrayOutputStream();
|
||||
super.encode(childrenOs);
|
||||
|
||||
os.write(utils.encode_varint(childrenOs.size()));
|
||||
os.write(childrenOs.toByteArray());
|
||||
}
|
||||
*/
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.protobuf.ProtobufUtils;
|
||||
|
||||
public class StringMessageField extends MessageField{
|
||||
String fieldValueString;
|
||||
|
||||
public StringMessageField(int fieldNumber, String value) {
|
||||
super(fieldNumber, FieldType.LENGTH_DELIMITED);
|
||||
fieldValueString = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteArrayOutputStream os) throws IOException {
|
||||
os.write(getStartBytes());
|
||||
|
||||
os.write(ProtobufUtils.encode_varint(fieldValueString.length()));
|
||||
|
||||
os.write(fieldValueString.getBytes());
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.protobuf.ProtobufUtils;
|
||||
|
||||
public class VarintMessageField extends MessageField{
|
||||
int fieldValueInt;
|
||||
|
||||
public VarintMessageField(int fieldNumber, int value){
|
||||
super(fieldNumber, FieldType.VARINT);
|
||||
this.fieldValueInt = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void encode(ByteArrayOutputStream os) throws IOException {
|
||||
os.write(getStartBytes());
|
||||
|
||||
os.write(ProtobufUtils.encode_varint(fieldValueInt));
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.test;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields.NestedMessageField;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields.RootMessageField;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields.StringMessageField;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.protobuf.messagefields.VarintMessageField;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.protobuf.ProtobufUtils;
|
||||
|
||||
public class ProtobufTest extends TestCase {
|
||||
private void assertEncodeVarint(byte[] expected, int valueToEncode){
|
||||
byte[] encoded = ProtobufUtils.encode_varint(valueToEncode);
|
||||
assertEquals(expected.length, encoded.length);
|
||||
|
||||
for(int i = 0; i < expected.length; i++){
|
||||
assertEquals(expected[i], encoded[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void testEncodeVarint(){
|
||||
assertEncodeVarint(new byte[]{0x00}, 0);
|
||||
assertEncodeVarint(new byte[]{0x01}, 1);
|
||||
assertEncodeVarint(new byte[]{0b1111111}, 0b1111111);
|
||||
assertEncodeVarint(new byte[]{(byte)0b11111111, 0b00000001}, 0b11111111);
|
||||
assertEncodeVarint(new byte[]{(byte)0xeb, (byte)0x86, (byte)0x4e}, 1278827);
|
||||
assertEncodeVarint(new byte[]{(byte)0xf9, (byte) 0xa5 , (byte) 0x3c}, 987897);
|
||||
assertEncodeVarint(new byte[]{(byte) 0xfd, (byte) 0xe5, (byte) 0x90, (byte) 0x01}, 2372349);
|
||||
}
|
||||
|
||||
private void assertEncodeVarintMessageField(byte[] expected, int fieldId, int value) throws IOException {
|
||||
VarintMessageField field = new VarintMessageField(fieldId, value);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
field.encode(os);
|
||||
byte[] result = os.toByteArray();
|
||||
|
||||
assertEquals(expected.length, result.length);
|
||||
|
||||
for(int i = 0; i < expected.length; i++){
|
||||
assertEquals(expected[i], result[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void testEncodeVarintMessageField() throws IOException {
|
||||
assertEncodeVarintMessageField(new byte[]{0b00001000, 0b00000000}, 1, 0);
|
||||
assertEncodeVarintMessageField(new byte[]{(byte) 0b10000000, 0b00000001, 0b01111111}, 16, 127);
|
||||
assertEncodeVarintMessageField(new byte[]{(byte) 0b10000000, 0b00000001, (byte) 0xfd, (byte) 0xe5, (byte) 0x90, (byte) 0x01}, 16, 2372349);
|
||||
}
|
||||
|
||||
private void assertEncodeStringMessageField(byte[] expected, int fieldId, String value) throws IOException {
|
||||
StringMessageField field = new StringMessageField(fieldId, value);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
field.encode(os);
|
||||
byte[] result = os.toByteArray();
|
||||
|
||||
assertEquals(expected.length, result.length);
|
||||
|
||||
for(int i = 0; i < expected.length; i++){
|
||||
assertEquals(expected[i], result[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertEncodeRootMessageField(byte[] expected, RootMessageField value) throws IOException {
|
||||
byte[] result = value.encodeToBytes();
|
||||
|
||||
System.out.println(StringUtils.bytesToHex(result));
|
||||
|
||||
assertEquals(expected.length, result.length);
|
||||
|
||||
for(int i = 0; i < expected.length; i++){
|
||||
assertEquals(expected[i], result[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void testEncodeStringMessageField() throws IOException {
|
||||
assertEncodeStringMessageField(new byte[]{0b00001010, 0b00000000}, 1, "");
|
||||
assertEncodeStringMessageField(new byte[]{0b01010010, 0x04, 0x74, 0x65, 0x73, 0x74}, 10, "test");
|
||||
}
|
||||
|
||||
public void testNestedMessageField() throws IOException {
|
||||
RootMessageField startAppRequest = new RootMessageField(
|
||||
new VarintMessageField(1, 2),
|
||||
new NestedMessageField(16,
|
||||
new StringMessageField(1, "Infrared"),
|
||||
new StringMessageField(2, "RPC")
|
||||
)
|
||||
);
|
||||
assertEncodeRootMessageField(
|
||||
new byte[]{(byte) 0x14, (byte) 0x08, (byte) 0x02, (byte) 0x82, (byte) 0x01, (byte) 0x0f, (byte) 0x0a, (byte) 0x08, (byte) 0x49, (byte) 0x6e, (byte) 0x66, (byte) 0x72, (byte) 0x61, (byte) 0x72, (byte) 0x65, (byte) 0x64, (byte) 0x12, (byte) 0x03, (byte) 0x52, (byte) 0x50, (byte) 0x43},
|
||||
startAppRequest
|
||||
);
|
||||
|
||||
RootMessageField loadFileRequest = new RootMessageField(
|
||||
new VarintMessageField(1, 3),
|
||||
new NestedMessageField(48,
|
||||
new StringMessageField(1, "/any/infrared/Remote.ir")
|
||||
)
|
||||
);
|
||||
assertEncodeRootMessageField(
|
||||
new byte[]{(byte) 0x1e, (byte) 0x08, (byte) 0x03, (byte) 0x82, (byte) 0x03, (byte) 0x19, (byte) 0x0a, (byte) 0x17, (byte) 0x2f, (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x2f, (byte) 0x69, (byte) 0x6e, (byte) 0x66, (byte) 0x72, (byte) 0x61, (byte) 0x72, (byte) 0x65, (byte) 0x64, (byte) 0x2f, (byte) 0x52, (byte) 0x65, (byte) 0x6d, (byte) 0x6f, (byte) 0x74, (byte) 0x65, (byte) 0x2e, (byte) 0x69, (byte) 0x72},
|
||||
loadFileRequest
|
||||
);
|
||||
|
||||
RootMessageField pressButtonRequest = new RootMessageField(
|
||||
new VarintMessageField(1, 4),
|
||||
new NestedMessageField(49,
|
||||
new StringMessageField(1, "Pwr")
|
||||
)
|
||||
);
|
||||
assertEncodeRootMessageField(new byte[]{(byte) 0x0a, (byte) 0x08, (byte) 0x04, (byte) 0x8a, (byte) 0x03, (byte) 0x05, (byte) 0x0a, (byte) 0x03, (byte) 0x50, (byte) 0x77, (byte) 0x72}, pressButtonRequest);
|
||||
|
||||
RootMessageField releaseButtonRequest = new RootMessageField(
|
||||
new VarintMessageField(1, 5),
|
||||
new NestedMessageField(50)
|
||||
);
|
||||
assertEncodeRootMessageField(new byte[]{(byte) 0x05, (byte) 0x08, (byte) 0x05, (byte) 0x92, (byte) 0x03, (byte) 0x00}, releaseButtonRequest);
|
||||
|
||||
RootMessageField exitAppRequest = new RootMessageField(
|
||||
new VarintMessageField(1, 6),
|
||||
new NestedMessageField(47)
|
||||
);
|
||||
assertEncodeRootMessageField(new byte[]{(byte) 0x05, (byte) 0x08, (byte) 0x06, (byte) 0xfa, (byte) 0x02, (byte) 0x00}, exitAppRequest);
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user