<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Protobuf Serialisation
This notebook documents how Acton serialises protobufs.
Protobufs can be serialised and deserialised individually using the built-in methods SerializeToString and ParseFromString
Step1:  To serialise multiple protobufs into one file, we serialise each to a string, write the length of this string to a file, then write the string to the file. The length is needed because protobufs are not self-delimiting. We use an unsigned long long with the struct library to store the length.
Step2:  We also want to store metadata in the resulting file. This is achieved by encoding the metadata as a bytestring and writing it before we write any protobufs. As with protobufs, we must store the length of the metadata before the metadata itself, and we again use an unsigned long long.
Reading the files back in is the inverse of the above; we simply unpack instead of packing and call ParseFromString.
<ASSISTANT_TASK:>
Python Code:
# Serialising.
with open(path, 'wb') as proto_file:
    proto_file.write(proto.SerializeToString())
# Deserialising. (from acton.proto.io)
proto = Proto()
with open(path, 'rb') as proto_file:
    proto.ParseFromString(proto_file.read())
Explanation: Protobuf Serialisation
This notebook documents how Acton serialises protobufs.
Protobufs can be serialised and deserialised individually using the built-in methods SerializeToString and ParseFromString:
End of explanation
for proto in protos:
    proto = proto.SerializeToString()
    length = struct.pack('<Q', len(proto))
    proto_file.write(length)
    proto_file.write(proto)
Explanation: To serialise multiple protobufs into one file, we serialise each to a string, write the length of this string to a file, then write the string to the file. The length is needed because protobufs are not self-delimiting. We use an unsigned long long with the struct library to store the length.
End of explanation
length = proto_file.read(8)  # 8 = long long
while length:
    length, = struct.unpack('<Q', length)
    proto = Proto()
    proto.ParseFromString(proto_file.read(length))
    length = proto_file.read(8)
Explanation: We also want to store metadata in the resulting file. This is achieved by encoding the metadata as a bytestring and writing it before we write any protobufs. As with protobufs, we must store the length of the metadata before the metadata itself, and we again use an unsigned long long.
Reading the files back in is the inverse of the above; we simply unpack instead of packing and call ParseFromString.
End of explanation
<END_TASK>