mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 02:56:37 +00:00

- added unittests for the raw_fd_stream output case. - the `BitstreamWriter` ctor was confusing, the relationship between the buffer and the file stream wasn't clear and in fact there was a potential bug in `BitcodeWriter` in the mach-o case, because that code assumed in-buffer only serialization. The incremental flushing behavior of flushing at end of block boundaries was an implementation detail that meant serializers not using blocks (for example) would need to know to check the buffer and flush. The bug was latent - in the sense that, today, because the stream being passed was not a `raw_fd_stream`, incremental buffering never kicked in. The new design moves the responsibility of flushing to the `BitstreamWriter`, and makes it work with any `raw_ostream` (but incrementally flush only in the `raw_fd_stream` case). If the `raw_ostream` is over a buffer - i.e. a `raw_svector_stream` - then it's equivalent to today's buffer case. For all other `raw_ostream` cases, buffering is an implementation detail. In all cases, the buffer is flushed (well, in the buffer case, that's a moot statement). This simplifies the state and state transitions the user has to track: you have a raw_ostream -> BitstreamWrite in it -> destroy the writer => the bitstream is completely written in your raw_ostream. The "buffer" case and the "raw_fd_stream" case become optimizations rather than imposing state transition concerns to the user.
139 lines
4.2 KiB
C++
139 lines
4.2 KiB
C++
//===- BitstreamWriterTest.cpp - Tests for BitstreamWriter ----------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Bitstream/BitstreamWriter.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Bitstream/BitCodeEnums.h"
|
|
#include "llvm/Bitstream/BitstreamReader.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Testing/Support/SupportHelpers.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
TEST(BitstreamWriterTest, emitBlob) {
|
|
SmallString<64> Buffer;
|
|
BitstreamWriter W(Buffer);
|
|
W.emitBlob("str", /* ShouldEmitSize */ false);
|
|
EXPECT_EQ(StringRef("str\0", 4), Buffer);
|
|
}
|
|
|
|
TEST(BitstreamWriterTest, emitBlobWithSize) {
|
|
SmallString<64> Buffer;
|
|
{
|
|
BitstreamWriter W(Buffer);
|
|
W.emitBlob("str");
|
|
}
|
|
SmallString<64> Expected;
|
|
{
|
|
BitstreamWriter W(Expected);
|
|
W.EmitVBR(3, 6);
|
|
W.FlushToWord();
|
|
W.Emit('s', 8);
|
|
W.Emit('t', 8);
|
|
W.Emit('r', 8);
|
|
W.Emit(0, 8);
|
|
}
|
|
EXPECT_EQ(Expected.str(), Buffer);
|
|
}
|
|
|
|
TEST(BitstreamWriterTest, emitBlobEmpty) {
|
|
SmallString<64> Buffer;
|
|
BitstreamWriter W(Buffer);
|
|
W.emitBlob("", /* ShouldEmitSize */ false);
|
|
EXPECT_EQ(StringRef(""), Buffer);
|
|
}
|
|
|
|
TEST(BitstreamWriterTest, emitBlob4ByteAligned) {
|
|
SmallString<64> Buffer;
|
|
BitstreamWriter W(Buffer);
|
|
W.emitBlob("str0", /* ShouldEmitSize */ false);
|
|
EXPECT_EQ(StringRef("str0"), Buffer);
|
|
}
|
|
|
|
class BitstreamWriterFlushTest : public ::testing::TestWithParam<int> {
|
|
protected:
|
|
// Any value after bitc::FIRST_APPLICATION_BLOCKID is good, but let's pick a
|
|
// distinctive one.
|
|
const unsigned BlkID = bitc::FIRST_APPLICATION_BLOCKID + 17;
|
|
|
|
void write(StringRef TestFilePath, int FlushThreshold,
|
|
llvm::function_ref<void(BitstreamWriter &)> Action) {
|
|
std::error_code EC;
|
|
raw_fd_stream Out(TestFilePath, EC);
|
|
ASSERT_FALSE(EC);
|
|
BitstreamWriter W(Out, FlushThreshold);
|
|
Action(W);
|
|
}
|
|
};
|
|
|
|
TEST_P(BitstreamWriterFlushTest, simpleExample) {
|
|
llvm::unittest::TempFile TestFile("bitstream", "", "",
|
|
/*Unique*/ true);
|
|
write(TestFile.path(), GetParam(),
|
|
[&](BitstreamWriter &W) { W.EmitVBR(42, 2); });
|
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
|
|
MemoryBuffer::getFile(TestFile.path());
|
|
ASSERT_TRUE(!!MB);
|
|
ASSERT_NE(*MB, nullptr);
|
|
BitstreamCursor Cursor((*MB)->getBuffer());
|
|
auto V = Cursor.ReadVBR(2);
|
|
EXPECT_TRUE(!!V);
|
|
EXPECT_EQ(*V, 42U);
|
|
}
|
|
|
|
TEST_P(BitstreamWriterFlushTest, subBlock) {
|
|
llvm::unittest::TempFile TestFile("bitstream", "", "",
|
|
/*Unique*/ true);
|
|
write(TestFile.path(), GetParam(), [&](BitstreamWriter &W) {
|
|
W.EnterSubblock(BlkID, 2);
|
|
W.EmitVBR(42, 2);
|
|
W.ExitBlock();
|
|
});
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
|
|
MemoryBuffer::getFile(TestFile.path());
|
|
ASSERT_TRUE(!!MB);
|
|
ASSERT_NE(*MB, nullptr);
|
|
BitstreamCursor Cursor((*MB)->getBuffer());
|
|
auto Blk = Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs);
|
|
ASSERT_TRUE(!!Blk);
|
|
EXPECT_EQ(Blk->Kind, BitstreamEntry::SubBlock);
|
|
EXPECT_EQ(Blk->ID, BlkID);
|
|
EXPECT_FALSE(Cursor.EnterSubBlock(BlkID));
|
|
auto V = Cursor.ReadVBR(2);
|
|
EXPECT_TRUE(!!V);
|
|
EXPECT_EQ(*V, 42U);
|
|
// ReadBlockEnd() returns false if it actually read the block end.
|
|
EXPECT_FALSE(Cursor.ReadBlockEnd());
|
|
EXPECT_TRUE(Cursor.AtEndOfStream());
|
|
}
|
|
|
|
TEST_P(BitstreamWriterFlushTest, blobRawRead) {
|
|
llvm::unittest::TempFile TestFile("bitstream", "", "",
|
|
/*Unique*/ true);
|
|
write(TestFile.path(), GetParam(), [&](BitstreamWriter &W) {
|
|
W.emitBlob("str", /* ShouldEmitSize */ false);
|
|
});
|
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
|
|
MemoryBuffer::getFile(TestFile.path());
|
|
ASSERT_TRUE(!!MB);
|
|
ASSERT_NE(*MB, nullptr);
|
|
EXPECT_EQ(StringRef("str\0", 4), (*MB)->getBuffer());
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(BitstreamWriterFlushCases, BitstreamWriterFlushTest,
|
|
::testing::Values(0, 1 /*MB*/));
|
|
} // end namespace
|