40.1 SerialElementWriter

SerialElementWriter is used by LssTxn to write a serial element (i.e. a packet chain) to the LSS.

The entire serial element is uniquely identified by a Seid, and this identifies the head data packet. Additional Seids may be allocated in order to identify the overflow packets in the packet chain.

SerialElementWriter implements ICloseableOutputStream. As a client makes calls to WriteStream the data is written into the segment being prepared in memory.

As an output stream, the SerialElementWriter is opened, written to, then closed.

The packet header contains the Seid of the next packet in the chain and the size of the packet. This presents a problem because we don't know these values at the time we write the header to the segment being prepared in memory. There are two solutions:

  • Buffer the data packet in a temporary area so we know how big it is; or
  • Write incomplete packet headers to the segment being prepared in memory, and complete them later

We choose the second solution because it avoids the need for additional buffering.

The SegmentWriter exposes the current segment being written, and a Segment exposes the raw buffer. With random access to the segment buffer, the SerialElementWriter can easily go back and complete the details in the packet header.

Avoiding unaligned memory accesses

ARMv7 doesn't allow for unaligned memory accesses. Therefore we cannot use "packed" structures for the packet log record header. Instead when writing the packet header we need to use an OutputArchive. This currently uses memcpy for basic types like int64. So although we haven't yet gone as far as supporting big endian machines, we can at least support a little endian ARMv7. Also, it is fairly straightforward to make OutputArchive support big endian, that is actually half way there already.

There are two private methods involved with writing the packet header:


void BeginWritePacket();
void EndWritePacket(Seid nextSeid);     // Write the packet header, update the RPM and SUT

BeginWritePacket records a pointer to the start of the packet log record then offsets the write position in memory by 13 bytes without writing the header. The header is written later in EndWritePacket using an OutputArchive. This needs to be done after writing the data because the packet header records the number of bytes in the data.

Code


class SerialElementWriter : public ICloseableOutputStream
{
public:
    SerialElementWriter(LSS& lss);
    void Open(Seid seid);
    bool IsOpen() const;
    Seid GetSeidBeingWritten() const;
    virtual void FlushStream() {}
    virtual void WriteStream(const void* buffer, ssize_t numBytes);
    virtual void Close();

private:
    void BeginWritePacket();
    void EndWritePacket(Seid nextSeid);     // Write the packet header, update the RPM and SUT

private:
    LSS& lss_;
    SegmentBeingWrittenInMemory& sm_;
    bool isOpen_ = false;
    LogRecordPosition lrPos_;                  // Position of the packet
    Seid seid_;
    bool isHeadOfChain_ = false;
    octet_t* startOfPacketLogRecord_ = nullptr;
    int64 totalNumBytesWritten_ = 0;           // Used to calculate the total number of octets in the serial element
    mutable std::mutex mutex_;
    Seid seidBeingWritten_;
};