首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >riff块-用于处理RIFF块的实用类

riff块-用于处理RIFF块的实用类
EN

Code Review用户
提问于 2020-07-10 21:15:51
回答 1查看 82关注 0票数 3

对于一个个人项目(读取媒体文件元数据),我创建了一个用于处理RIFF块的小型/快速实用程序类。

支持:

  • 用32位积分值定义块
  • 使用4个ASCII字符定义块
  • 将块分割成2 16位整数值(用于解析RIFF报头)
  • 比较块是否相等

不支持(可以添加,但我不需要它用于只用于windows的家庭项目):

  • 终态变化

头文件:

代码语言:javascript
复制
#include 
#include 
#include 
#include 

namespace wpcs::riff
{
    /// @brief chunk of data (as specified by RIFF format)
    ///
    /// @see http://soundfile.sapp.org/doc/WaveFormat/
    class chunk final
    {
        std::array data_;

    public:
        explicit chunk(std::uint32_t data = {});

        std::string string() const;
        std::uint32_t uint32() const;
        std::array uint16s() const;

        inline bool operator==(chunk const &x) const
        {
            return data_ == x.data_;
        }

        inline bool operator!=(chunk const &x) const
        {
            return data_ != x.data_;
        }
    };

    inline namespace literals
    {
        inline auto operator""_chunk(char const *const str, std::size_t size)
        {
            if (size != 4)
            {
                std::string msg{"Bad chunk: "};
                std::copy(str, str + size, msg.end());
                throw std::logic_error{msg};
            }
            auto value = std::uint32_t{};
            std::copy(str, str + size, reinterpret_cast(&value));
            return chunk{value};
        }

        inline auto operator""_chunk(unsigned long long x)
        {
            return chunk{static_cast(x)};
        }
    } // namespace literals

} // namespace wpcs::riff

实施文件:

代码语言:javascript
复制
#include 

namespace wpcs::riff
{
    chunk::chunk(std::uint32_t data)
        : data_{}
    {
        auto start = reinterpret_cast(&data);
        auto stop = start + sizeof(data);
        std::copy(start, stop, begin(data_));
    }

    std::uint32_t chunk::uint32() const
    {
        return *reinterpret_cast(&data_[0]);
    }

    std::string chunk::string() const
    {
        constexpr auto const zero = std::byte{0};
        constexpr auto const null_char = '.';

        auto result = std::string(4, null_char);
        std::transform(begin(data_), end(data_), begin(result), [&](std::byte b) {
            if (b == zero)
                return null_char;
            return static_cast(b);
        });
        return result;
    }

    std::array chunk::uint16s() const
    {
        auto result = std::array{};

        result[0] = static_cast(data_[1]);
        result[0] <<= 8;
        result[0] += static_cast(data_[0]);

        result[1] = static_cast(data_[3]);
        result[1] <<= 8;
        result[1] += static_cast(data_[2]);

        return result;
    }

} // namespace wpcs::riff

使用(使用catch2编写的测试):

代码语言:javascript
复制
#define CATCH_CONFIG_MAIN
#include 

#include 

TEST_CASE("wpcs::riff::chunk", "[chunk]")
{
    GIVEN("an empty chunk")
    {
        auto const c = wpcs::riff::chunk{};

        WHEN("a string representation is created")
        {
            THEN("the string is filled with dots (representing null clars)")
            {
                REQUIRE(c.string() == "....");
            }
        }

        WHEN("a unit32 representation is created")
        {
            THEN("the representation is filled with zeros")
            {
                auto const value = c.uint32();
                REQUIRE(value == std::uint32_t{});
            }
        }

        WHEN("a uint16 representation is created")
        {
            THEN("the representation contains two zeros")
            {
                auto const values = c.uint16s();
                REQUIRE(values.size() == 2);
                REQUIRE(values[0] == std::uint16_t{});
                REQUIRE(values[1] == std::uint16_t{});
            }
        }
    }

    GIVEN("a user defined string literal representation of a chunk")
    {
        using namespace wpcs::riff::literals;

        auto const c = "WAVE"_chunk;

        THEN("it's representations are correct")
        {
            REQUIRE(c.uint32() == 0x45564157);
            REQUIRE(c.string() == "WAVE");

            auto const short_ints = c.uint16s();
            REQUIRE(short_ints[0] == 0x4157);
            REQUIRE(short_ints[1] == 0x4556);
        }
    }

    GIVEN("a user defined numeric literal representation of a chunk")
    {
        using namespace wpcs::riff::literals;

        auto const c = 1163280727_chunk;

        THEN("it's representations are correct")
        {
            REQUIRE(c.uint32() == 1163280727);
            REQUIRE(c.string() == "WAVE");

            auto const short_ints = c.uint16s();
            REQUIRE(short_ints[0] == 0x4157);
            REQUIRE(short_ints[1] == 0x4556);
        }
    }
}

请检查可能的代码改进和代码样式。

EN

回答 1

Code Review用户

回答已采纳

发布于 2020-07-11 22:47:16

--这并不表示RIFF块

一个RIFF块不仅仅是4字节标识符。块由头、数据和填充字节组成。此外,标头包含标识符以及数据的大小。

是标识符uin32_t或4 std::bytes?

的数组。

将标识符内部存储为std::array有点奇怪,但是没有一个公共成员函数使用这种格式。许多代码是不必要的复杂,仅仅是因为您必须转换到和从该格式。

我要么在内部将其存储为uint32_t,以便与传递给构造函数的内容相匹配,要么因为RIFF块标识符由4个ASCII字符组成,因此我只需将其定义为char id[4]

不必要地使用用户定义的文字

与其声明用户定义的文本,我只需要重载构造函数,将C字符串作为参数。这样,您就可以代替"WAVE"_chunk编写chunk("WAVE"),它的长度大致相同,而且也不那么令人惊讶。

非对齐访问

您使用类型双关将std::array转换为uint32_t,但不能保证std::arrayuint32_t具有相同的对齐方式。虽然在堆栈上声明的chunk在实践中可能正确地对齐,但在chunk没有像您预期的那样对齐的情况下并不那么困难。例如,考虑:

代码语言:javascript
复制
struct foo {
    char x;
    wcps::riff::chunk chunk;
};

您会发现sizeof(struct foo)是5,offsetoff(struct foo, chunk)是1。使用类型双关转换为uint32_t是未定义的行为,并且可能会在不支持未对齐负载和存储的平台上崩溃。

编写越界

在此代码中:

代码语言:javascript
复制
std::string msg{"Bad chunk: "};
std::copy(str, str + size, msg.end());
throw std::logic_error{msg};

您将显式地将str复制到msg结束后。这是缓冲区溢出。相反,我只会写:

代码语言:javascript
复制
throw std::logic_error(std::string("Bad chunk: ") + str::string(str, size));
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/245297

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档