Skip to content

Commit

Permalink
feat: reduce unnecessary copies of vectors
Browse files Browse the repository at this point in the history
  • Loading branch information
craftablescience committed Jun 2, 2024
1 parent f4028b6 commit b87ad96
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 50 deletions.
23 changes: 14 additions & 9 deletions include/vtfpp/vtfpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ constexpr uint32_t VTF_SIGNATURE = 'V' + ('T' << 8) + ('F' << 16);

struct Resource {
enum Type : uint8_t {
TYPE_UNKNOWN = '\0', // Unknown
TYPE_THUMBNAIL_DATA = '\x01', // \x01\0\0
TYPE_IMAGE_DATA = '\x30', // \x30\0\0
TYPE_PARTICLE_SHEET_DATA = '\x10', // \x10\0\0
Expand All @@ -34,15 +35,15 @@ struct Resource {
FLAG_NO_DATA = 1 << 1,
};

Type type;
Flags flags;
std::vector<std::byte> data;
Type type = TYPE_UNKNOWN;
Flags flags = FLAG_NONE;
std::span<std::byte> data;

using ConvertedData = std::variant<
std::monostate, // Anything that would be equivalent to just returning data directly, or used as an error
uint32_t, // CRC, TSO
std::pair<uint8_t, uint8_t>, // LOD
std::string_view // KVD
std::string // KVD
>;
[[nodiscard]] ConvertedData convertData() const;

Expand All @@ -58,8 +59,8 @@ struct Resource {
return std::get<std::pair<uint8_t, uint8_t>>(this->convertData());
}

[[nodiscard]] std::string_view getDataAsKeyValuesData() const {
return std::get<std::string_view>(this->convertData());
[[nodiscard]] std::string getDataAsKeyValuesData() const {
return std::get<std::string>(this->convertData());
}
};

Expand Down Expand Up @@ -102,14 +103,16 @@ class VTF {
FLAG_SPECVAR_ALPHA = 1 << 31, // Removed
};

VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly = false);

VTF(const unsigned char* vtfData, std::size_t vtfSize, bool parseHeaderOnly = false);
explicit VTF(std::vector<std::byte>&& vtfData, bool parseHeaderOnly = false);

explicit VTF(const std::vector<std::byte>& vtfData, bool parseHeaderOnly = false);

explicit VTF(const std::vector<unsigned char>& vtfData, bool parseHeaderOnly = false);

VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly = false);

VTF(const unsigned char* vtfData, std::size_t vtfSize, bool parseHeaderOnly = false);

[[nodiscard]] explicit operator bool() const;

[[nodiscard]] uint32_t getMajorVersion() const;
Expand Down Expand Up @@ -167,6 +170,8 @@ class VTF {
private:
bool opened = false;

std::vector<std::byte> data;

//uint32_t signature;
uint32_t majorVersion{};
uint32_t minorVersion{};
Expand Down
2 changes: 1 addition & 1 deletion src/thirdparty/bufferstream
20 changes: 10 additions & 10 deletions src/vtfpp/ImageConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ namespace {
if (CMP_ConvertTexture(&srcTexture, &destTexture, &options, nullptr) != CMP_OK) {
return {};
}
return destData;
return std::move(destData);
}

[[nodiscard]] std::vector<std::byte> convertImageDataToRGBA8888(std::span<const std::byte> imageData, ImageFormat format) {
Expand Down Expand Up @@ -241,7 +241,7 @@ namespace {
default:
break;
}
return newData;
return std::move(newData);
}

[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA8888(std::span<const std::byte> imageData, ImageFormat format) {
Expand Down Expand Up @@ -413,15 +413,15 @@ namespace {
default:
break;
}
return newData;
return std::move(newData);
}

[[nodiscard]] std::vector<std::byte> decodeImageDataToRGBA8888(std::span<const std::byte> imageData, ImageFormat format, uint16_t width, uint16_t height) {
return ::convertImageDataUsingCompressonator(imageData, format, ImageFormat::RGBA8888, width, height);
return std::move(::convertImageDataUsingCompressonator(imageData, format, ImageFormat::RGBA8888, width, height));
}

[[nodiscard]] std::vector<std::byte> encodeImageDataFromRGBA8888(std::span<const std::byte> imageData, ImageFormat format, uint16_t width, uint16_t height) {
return ::convertImageDataUsingCompressonator(imageData, ImageFormat::RGBA8888, format, width, height);
return std::move(::convertImageDataUsingCompressonator(imageData, ImageFormat::RGBA8888, format, width, height));
}

[[nodiscard]] std::vector<std::byte> convertImageDataToRGBA32323232F(std::span<const std::byte> imageData, ImageFormat format) {
Expand Down Expand Up @@ -473,15 +473,15 @@ std::vector<std::byte> ImageConversion::convertImageDataToFormat(std::span<const
}

if (intermediaryFormat == newFormat) {
return intermediaryData;
return std::move(intermediaryData);
}
if (intermediaryFormat == ImageFormat::RGBA8888) {
if (ImageFormatDetails::compressed(newFormat)) {
return ::encodeImageDataFromRGBA8888(intermediaryData, newFormat, width, height);
return std::move(::encodeImageDataFromRGBA8888(intermediaryData, newFormat, width, height));
}
return ::convertImageDataFromRGBA8888(intermediaryData, newFormat);
return std::move(::convertImageDataFromRGBA8888(intermediaryData, newFormat));
}
return ::convertImageDataFromRGBA32323232F(intermediaryData, newFormat);
return std::move(::convertImageDataFromRGBA32323232F(intermediaryData, newFormat));
}

std::vector<std::byte> ImageConversion::convertImageDataToFile(std::span<const std::byte> imageData, ImageFormat format, uint16_t width, uint16_t height) {
Expand All @@ -498,5 +498,5 @@ std::vector<std::byte> ImageConversion::convertImageDataToFile(std::span<const s
auto rgba = convertImageDataToFormat(imageData, format, ImageFormat::RGBA8888, width, height);
stbi_write_png_to_func(stbWriteFunc, &out, width, height, 4, rgba.data(), 0);
}
return out;
return std::move(out);
}
55 changes: 26 additions & 29 deletions src/vtfpp/vtfpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,18 @@ Resource::ConvertedData Resource::convertData() const {
);
case TYPE_KEYVALUES_DATA:
if (this->data.size() <= 4) {
return std::string_view{""};
return "";
}
return std::string_view{reinterpret_cast<const char*>(this->data.data() + 4), *reinterpret_cast<const uint32_t*>(this->data.data()) + 1};
return std::string(reinterpret_cast<const char*>(this->data.data() + 4), *reinterpret_cast<const uint32_t*>(this->data.data()));
default:
break;
}
return {};
}

VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) {
BufferStreamReadOnly stream{vtfData, vtfSize};
VTF::VTF(std::vector<std::byte>&& vtfData, bool parseHeaderOnly)
: data(std::move(vtfData)) {
BufferStreamReadOnly stream{this->data};

if (stream.read<uint32_t>() != VTF_SIGNATURE) {
return;
Expand Down Expand Up @@ -94,7 +95,7 @@ VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) {
.read(resource->type)
.skip(2)
.read(resource->flags);
resource->data = stream.read_bytes(4);
resource->data = stream.read_span<std::byte>(4);

if (!(resource->flags & Resource::FLAG_NO_DATA)) {
if (lastResourceIndex >= 0) {
Expand All @@ -103,7 +104,7 @@ VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) {

auto curPos = stream.tell();
stream.seek(lastOffset);
this->resources[lastResourceIndex]->data = stream.read_bytes(currentOffset - lastOffset);
this->resources[lastResourceIndex]->data = stream.read_span<std::byte>(currentOffset - lastOffset);
stream.seek(curPos);
}
lastResourceIndex = static_cast<int>(this->resources.size() - 1);
Expand All @@ -113,17 +114,10 @@ VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) {
auto offset = *reinterpret_cast<uint32_t*>(this->resources[lastResourceIndex]->data.data());
auto curPos = stream.tell();
stream.seek(offset);
this->resources[lastResourceIndex]->data = stream.read_bytes(stream.size() - offset);
this->resources[lastResourceIndex]->data = stream.read_span<std::byte>(stream.size() - offset);
stream.seek(curPos);
}

// Null-terminate KeyValues data so std::string_view works
for (auto& resource : this->resources) {
if (resource->type == Resource::TYPE_KEYVALUES_DATA) {
resource->data.push_back(std::byte{'\0'});
}
}

this->opened = stream.tell() == headerLength;
} else {
while (stream.tell() % 16 != 0) {
Expand All @@ -133,23 +127,26 @@ VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly) {

if (this->thumbnailWidth > 0 && this->thumbnailHeight > 0) {
auto thumbnailLength = ImageFormatDetails::getDataLength(this->thumbnailFormat, this->thumbnailWidth, this->thumbnailHeight);
this->resources.emplace_back(new Resource{Resource::TYPE_THUMBNAIL_DATA, Resource::FLAG_NONE, stream.read_bytes(thumbnailLength)});
this->resources.emplace_back(new Resource{Resource::TYPE_THUMBNAIL_DATA, Resource::FLAG_NONE, stream.read_span<std::byte>(thumbnailLength)});
}
if (this->width > 0 && this->height > 0) {
auto imageLength = ImageFormatDetails::getDataLength(this->format, this->mipCount, this->frameCount, this->getFaceCount(), this->width, this->height, this->sliceCount);
this->resources.emplace_back(new Resource{Resource::TYPE_IMAGE_DATA, Resource::FLAG_NONE, stream.read_bytes(imageLength)});
this->resources.emplace_back(new Resource{Resource::TYPE_IMAGE_DATA, Resource::FLAG_NONE, stream.read_span<std::byte>(imageLength)});
}
}
}

VTF::VTF(const unsigned char* vtfData, std::size_t vtfSize, bool parseHeaderOnly)
: VTF(reinterpret_cast<const std::byte*>(vtfData), vtfSize, parseHeaderOnly) {}

VTF::VTF(const std::vector<std::byte>& vtfData, bool parseHeaderOnly)
: VTF(vtfData.data(), vtfData.size(), parseHeaderOnly) {}
: VTF(std::vector<std::byte>{vtfData.begin(), vtfData.end()}, parseHeaderOnly) {}

VTF::VTF(const std::vector<unsigned char>& vtfData, bool parseHeaderOnly)
: VTF(vtfData.data(), vtfData.size(), parseHeaderOnly) {}
: VTF(std::vector<std::byte>{reinterpret_cast<const std::byte*>(vtfData.data()), reinterpret_cast<const std::byte*>(vtfData.data() + vtfData.size())}, parseHeaderOnly) {}

VTF::VTF(const std::byte* vtfData, std::size_t vtfSize, bool parseHeaderOnly)
: VTF(std::vector<std::byte>{vtfData, vtfData + vtfSize}, parseHeaderOnly) {}

VTF::VTF(const unsigned char* vtfData, std::size_t vtfSize, bool parseHeaderOnly)
: VTF(std::vector<std::byte>{reinterpret_cast<const std::byte*>(vtfData), reinterpret_cast<const std::byte*>(vtfData + vtfSize)}, parseHeaderOnly) {}

VTF::operator bool() const {
return this->opened;
Expand Down Expand Up @@ -238,7 +235,7 @@ std::span<const std::byte> VTF::getImageData(uint8_t mip, uint16_t frame, uint16
if (!ImageFormatDetails::getDataPosition(offset, length, this->format, mip, this->mipCount, frame, this->frameCount, face, this->getFaceCount(), this->width, this->height, slice, this->sliceCount)) {
return {};
}
return std::span<const std::byte>{imageResource->data}.subspan(offset, length);
return imageResource->data.subspan(offset, length);
}
return {};
}
Expand All @@ -248,24 +245,24 @@ std::vector<std::byte> VTF::getImageDataAs(ImageFormat newFormat, uint8_t mip, u
if (rawImageData.empty()) {
return {};
}
return ImageConversion::convertImageDataToFormat(rawImageData, this->format, newFormat, ImageDimensions::getMipDim(mip, this->width), ImageDimensions::getMipDim(mip, this->height));
return std::move(ImageConversion::convertImageDataToFormat(rawImageData, this->format, newFormat, ImageDimensions::getMipDim(mip, this->width), ImageDimensions::getMipDim(mip, this->height)));
}

std::vector<std::byte> VTF::getImageDataAsRGBA8888(uint8_t mip, uint16_t frame, uint16_t face, uint16_t slice) const {
return this->getImageDataAs(ImageFormat::RGBA8888, mip, frame, face, slice);
return std::move(this->getImageDataAs(ImageFormat::RGBA8888, mip, frame, face, slice));
}

std::vector<std::byte> VTF::convertAndSaveImageDataToFile(uint8_t mip, uint16_t frame, uint16_t face, uint16_t slice) const {
auto rawImageData = this->getImageData(mip, frame, face, slice);
if (rawImageData.empty()) {
return {};
}
return ImageConversion::convertImageDataToFile(rawImageData, this->format, ImageDimensions::getMipDim(mip, this->width), ImageDimensions::getMipDim(mip, this->height));
return std::move(ImageConversion::convertImageDataToFile(rawImageData, this->format, ImageDimensions::getMipDim(mip, this->width), ImageDimensions::getMipDim(mip, this->height)));
}

std::span<const std::byte> VTF::getThumbnailData() const {
if (auto thumbnailResource = this->getResource(Resource::TYPE_THUMBNAIL_DATA)) {
return {thumbnailResource->data};
return thumbnailResource->data;
}
return {};
}
Expand All @@ -275,17 +272,17 @@ std::vector<std::byte> VTF::getThumbnailDataAs(ImageFormat newFormat) const {
if (rawThumbnailData.empty()) {
return {};
}
return ImageConversion::convertImageDataToFormat(rawThumbnailData, this->thumbnailFormat, newFormat, this->thumbnailWidth, this->thumbnailHeight);
return std::move(ImageConversion::convertImageDataToFormat(rawThumbnailData, this->thumbnailFormat, newFormat, this->thumbnailWidth, this->thumbnailHeight));
}

std::vector<std::byte> VTF::getThumbnailDataAsRGBA8888() const {
return this->getThumbnailDataAs(ImageFormat::RGBA8888);
return std::move(this->getThumbnailDataAs(ImageFormat::RGBA8888));
}

std::vector<std::byte> VTF::convertAndSaveThumbnailDataToFile() const {
auto rawThumbnailData = this->getThumbnailData();
if (rawThumbnailData.empty()) {
return {};
}
return ImageConversion::convertImageDataToFile(rawThumbnailData, this->thumbnailFormat, this->thumbnailWidth, this->thumbnailHeight);
return std::move(ImageConversion::convertImageDataToFile(rawThumbnailData, this->thumbnailFormat, this->thumbnailWidth, this->thumbnailHeight));
}
2 changes: 1 addition & 1 deletion test/vtfpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,7 @@ TEST(vtfpp, read_v75) {
ASSERT_TRUE(keyValues);
EXPECT_EQ(keyValues->flags, Resource::FLAG_NONE);
auto keyValuesData = keyValues->getDataAsKeyValuesData();
EXPECT_STREQ(keyValuesData.data(), "\"Information\"\r\n{\r\n\t\"Author\" \"craftablescience\"\r\n\t\"Contact\" \"contact\"\r\n\t\"Version\" \"version\"\r\n\t\"Modification\" \"modification\"\r\n\t\"Description\" \"description\"\r\n\t\"Comments\" \"comments\"\r\n}\r\n");
EXPECT_STREQ(keyValuesData.c_str(), "\"Information\"\r\n{\r\n\t\"Author\" \"craftablescience\"\r\n\t\"Contact\" \"contact\"\r\n\t\"Version\" \"version\"\r\n\t\"Modification\" \"modification\"\r\n\t\"Description\" \"description\"\r\n\t\"Comments\" \"comments\"\r\n}\r\n");
}

TEST(vtfpp, read_v75_nomip) {
Expand Down

0 comments on commit b87ad96

Please sign in to comment.