对于一个个人项目(读取媒体文件元数据),我创建了一个用于处理RIFF块的小型/快速实用程序类。
支持:
不支持(可以添加,但我不需要它用于只用于windows的家庭项目):
头文件:
#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实施文件:
#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编写的测试):
#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);
}
}
}请检查可能的代码改进和代码样式。
发布于 2020-07-11 22:47:16
一个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::array与uint32_t具有相同的对齐方式。虽然在堆栈上声明的chunk在实践中可能正确地对齐,但在chunk没有像您预期的那样对齐的情况下并不那么困难。例如,考虑:
struct foo {
char x;
wcps::riff::chunk chunk;
};您会发现sizeof(struct foo)是5,offsetoff(struct foo, chunk)是1。使用类型双关转换为uint32_t是未定义的行为,并且可能会在不支持未对齐负载和存储的平台上崩溃。
在此代码中:
std::string msg{"Bad chunk: "};
std::copy(str, str + size, msg.end());
throw std::logic_error{msg};您将显式地将str复制到msg结束后。这是缓冲区溢出。相反,我只会写:
throw std::logic_error(std::string("Bad chunk: ") + str::string(str, size));https://codereview.stackexchange.com/questions/245297
复制相似问题