I'm trying to serialize data, which is specified by the following protobuf file:
syntax = "proto3";
package efficient_servers.protobuf;
message Request {
oneof msg {
Walk walk = 1;
OneToOne oneToOne = 2;
OneToAll oneToAll = 3;
Reset reset = 4;
}
}
message Walk {
repeated Location locations = 1; // repeated n times, determines edge directions
repeated uint32 lengths = 2; // [mm], repeated n-1 times
}
message OneToOne {
Location origin = 1;
Location destination = 2;
}
message OneToAll {
Location origin = 1;
}
message Reset {}
message Location {
int32 x = 1; // [mm]
int32 y = 2; // [mm]
}
message Response {
enum Status {OK = 0; ERROR = 1;};
Status status = 1; // Always present
string errMsg = 2;
uint64 shortest_path_length = 3; // [mm]
uint64 total_length = 4; // [mm]
}
I need to create a Response
, that has only the status field filled out with the OK
variant, the encoded_len()
function returns 0. To me, this seems, that the default values of the message are ignored by the encoder, which is understandable, it wants to minimize the message size. But what if I want to send just a message with a single default field? In thate case, the message size amounts to zero and no message is encoded.
Is there a way around this nonsense? Note that I cannot edit the .proto specification file.
I've tried using just the ERROR
variant of the status enum, and that returned a valid, non-zero encoded_len()
, but I also need to serialize to OK
variant.
I'm using the following code:
pub fn generate_response(data: Result<RequestResponse, String>) -> BytesMut {
let mut response = Response::default();
match data {
Ok(request_response) => {
match request_response.request {
Msg::Reset(_) => {
response.set_status(Status::Ok);
},
Msg::OneToAll(_) => {
response.set_status(Status::Ok);
response.shortest_path_length = request_response.length;
}
Msg::OneToOne(_) => {
response.set_status(Status::Ok);
response.total_length = request_response.length;
}
Msg::Walk(_) => {
response.set_status(Status::Ok);
},
}
}
Err(msg) => {
response.set_status(Status::Error);
response.err_msg = msg.parse().unwrap();
}
}
let encoded_len = response.encoded_len();
let mut buf = BytesMut::with_capacity(encoded_len);
buf.resize(encoded_len, 0);
response.encode(&mut buf).unwrap();
return buf;
}
To summarize, the encoded_len() function returns 0 when ResponseRequest.request is either Msg::Reset or Msg:Walk, which means, that no data i serialized, and therefore I have no data to send.
But what if I want to send just a message with a single default field? In that case, the message size amounts to zero and no message is encoded.
So you send a response with length 0, and the receiving side decodes that as a Response
, and you end up with a Response
message instance which just contains the default values.
That shouldn't cause any problems, assuming whatever transport you're using is capable of saying "the response has length X", and therefore is capable of handling an empty response.
Note that if you have a Response
field within some other message type, there's still a difference between "this field is populated with the default message" and "this field isn't populated" - you'd end up with the tag for the field and then the 0-byte length-prefixed message.