簡體   English   中英

C ++防御性編程:具有類型安全性的緩沖區讀取

[英]C++ defensive programming: reading from a buffer with type safety

假設我有一個不屬於自己的類:DataBuffer。 它提供了各種get成員函數:

get(uint8_t *value);
get(uint16_t *value); 
...

從此緩沖區中包含的結構讀取數據時,我知道字段的順序和大小,並且我想減少將來代碼更改導致錯誤的機會:

struct Record 
{
    uint16_t Header;
    uint16_t Content;
}

void ReadIntoRecord(Record* r)
{
    DataBuffer buf( initialized from the network with bytes )
    buf.get(&r->Header); // Good!
    buf.get(&r->Content);
}

然后有人檢查更改以對標頭做一些事情,然后再寫它:

    uint8_t customHeader;
    buf.get(&customHeader);  // Wrong, stopped reading after only 1 byte
    r->Header = customHeader + 1;
    buf.get(&r->Content);  // now we're reading from the wrong part of the buffer.

以下是強化代碼以防更改的可接受方法嗎? 記住,我不能將函數名稱更改為getByte,getUShort等。我可以從DataBuffer繼承,但這似乎有些過分。

    buf.get(static_cast<uint16_t*>(&r->Header));  // compiler will catch incorrect variable type
    buf.get(static_cast<uint16_t*>(&r->Content))

更新為不安全的舊代碼示例:

       float dummy_float;
        uint32_t dummy32;
        uint16_t dummy16;
        uint8_t dummy8;

        uint16_t headTypeTemp;
        buf.get(static_cast<uint16_t*>(&headTypeTemp));
        m_headType = HeadType(headTypeTemp);
        buf.get(static_cast<uint8_t*>(&hid));
        buf.get(m_Name);
        buf.get(m_SerialNumber);


        float start;
        buf.get(static_cast<float*>(&start));
        float stop;
        buf.get(static_cast<float*>(&stop));


        buf.get(static_cast<float*>(&dummy_float));
        setStuffA(dummy_float);

        buf.get(static_cast<uint16_t*>(&dummy16));
        setStuffB(float(dummy16)/1000);

        buf.get(static_cast<uint8_t*>(&dummy8));    //reserved





        buf.get(static_cast<uint32_t*>(&dummy32));
        Entries().setStart( dummy32 );
        buf.get(static_cast<uint32_t*>(&dummy32));
        Entries().setStop( dummy32 );
        buf.get(static_cast<float*>(&dummy_float));
        Entries().setMoreStuff( dummy_float );

        uint32_t datalength;
        buf.get(static_cast<uint32_t*>(&datalength));

        Entries().data().setLength(datalength);

        RetVal ret = ReturnCode::SUCCESS;
        Entry* data_ptr = Entries().data().data();
        for (unsigned int i = 0; i < datalength && ret == ReturnCode::SUCCESS; i++)
        {
            ret = buf.get(static_cast<float*>(&dummy_float));
            data_ptr[i].FieldA = dummy_float;
        }

        for (unsigned int i = 0; i < datalength && ret == ReturnCode::SUCCESS; i++)
        {
            ret = buf.get(static_cast<float*>(&dummy_float));
            data_ptr[i].FieldB = dummy_float;
        }

        // Read in the normalization vector
        Util::SimpleVector<float> norm;
        buf.get(static_cast<uint32_t*>(&datalength));
        norm.setLength(datalength);
        for (unsigned int i=0; i<datalength; i++)
        {
            norm[i] = buf.getFloat();
        }

        setNormalization(norm);

        return ReturnCode::SUCCESS;
}

不要使用重載。 為什么沒有get_wordget_dword調用? 界面不會更難看,但是至少錯誤很難制造。

從網絡上讀取整個結構會更好嗎? 讓用戶執行所有套接字操作對我來說似乎是個壞主意(未封裝)。 封裝要在網絡上發送的內容以對文件描述符進行操作,而不是讓用戶將原始緩沖區數據放入文件描述符中。

我可以想像像

void readHeader(int filedes, struct Record * Header);

所以你可以做這樣的事情

struct Record 
{
  uint16_t Header;
  uint16_t Content;
  uint16_t getHeader() const { return Header; }
  uint16_t getContent() const  { return Content; }  
};

/* socket stuff to get filedes */
struct Record x;
readHeader(fd, &x);
x.getContent();

除非緩沖區包含有關內容的信息,否則您不能從類型安全的緩沖區中讀取內容。 一種簡單的方法是增加每個結構的長度,並檢查至少要讀取的數據仍然是合理的長度。 您也可以使用XML或ASN.1或類似的提供類型信息的地方。 當然,我假設您也寫入該緩沖區。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM