-
Notifications
You must be signed in to change notification settings - Fork 903
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory-efficient zlib usage across Liberty file consumers #4834
Open
widlarizer
wants to merge
11
commits into
main
Choose a base branch
from
emil/gzip-refactor
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
78caf20
io: refactor string and file work into new unit
widlarizer 18eb297
gzip: istream
widlarizer 0c6c0d9
dfflibmap: allow gzipped liberty files
widlarizer 34f15e7
gzip: simplify uncompressed interface
widlarizer 2dfbe5a
io: remove unused unistd.h to fix windows build
widlarizer 8c0f876
dfflibmap: allow gzipped liberty files
widlarizer 5a5323a
io: smooth out non-POSIX function usage across platforms
widlarizer 5b70cea
gzip: minor refactor
widlarizer d7f16cd
gzip: uphold rules for basic_streambuf::underflow overrides
widlarizer a720747
gzip: back to pointers
widlarizer 3afbd48
stat: allow gzipped liberty files
widlarizer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
#include "kernel/yosys_common.h" | ||
#include "kernel/log.h" | ||
#include "kernel/gzip.h" | ||
#include <iostream> | ||
#include <string> | ||
#include <cstdarg> | ||
#include <cstdio> | ||
|
||
#if !defined(WIN32) | ||
#include <dirent.h> | ||
#include <unistd.h> | ||
#else | ||
#include <io.h> | ||
#endif | ||
|
||
YOSYS_NAMESPACE_BEGIN | ||
|
||
#ifdef YOSYS_ENABLE_ZLIB | ||
|
||
gzip_ostream::obuf::obuf() { | ||
setp(buffer, buffer + buffer_size - 1); | ||
} | ||
|
||
bool gzip_ostream::obuf::open(const std::string &filename) { | ||
gzf = Zlib::gzopen(filename.c_str(), "wb"); | ||
return gzf != nullptr; | ||
} | ||
|
||
int gzip_ostream::obuf::sync() { | ||
int num = pptr() - pbase(); | ||
if (num > 0) { | ||
if (Zlib::gzwrite(gzf, reinterpret_cast<const void*>(pbase()), num) != num) { | ||
return -1; | ||
} | ||
pbump(-num); | ||
} | ||
return 0; | ||
} | ||
|
||
gzip_ostream::obuf::~obuf() { | ||
if (gzf) { | ||
sync(); | ||
Zlib::gzclose(gzf); | ||
} | ||
} | ||
|
||
bool gzip_istream::ibuf::open(const std::string& filename) { | ||
if (gzf) { | ||
Zlib::gzclose(gzf); | ||
} | ||
gzf = Zlib::gzopen(filename.c_str(), "rb"); | ||
if (!gzf) { | ||
return false; | ||
} | ||
// Empty and point to start | ||
setg(buffer, buffer, buffer); | ||
return true; | ||
} | ||
|
||
// Called when the buffer is empty and more input is needed | ||
std::istream::int_type gzip_istream::ibuf::underflow() { | ||
log_assert(gzf && "No gzfile opened\n"); | ||
int bytes_read = Zlib::gzread(gzf, buffer, buffer_size); | ||
if (bytes_read <= 0) { | ||
if (Zlib::gzeof(gzf)) { | ||
// "On failure, the function ensures that either | ||
// gptr() == nullptr or gptr() == egptr." | ||
// Let's set gptr to egptr | ||
setg(eback(), egptr(), egptr()); | ||
return traits_type::eof(); | ||
} | ||
|
||
int err; | ||
const char* error_msg = Zlib::gzerror(gzf, &err); | ||
if (err != Z_OK) | ||
log_error("%s", error_msg); | ||
else | ||
log_error("Decompression logic failure: "\ | ||
"read <=0 bytes but neither EOF nor error\n"); | ||
} | ||
|
||
// Keep size and point to start | ||
setg(buffer, buffer, buffer + bytes_read); | ||
return traits_type::to_int_type(buffer[0]); | ||
} | ||
|
||
gzip_istream::ibuf::~ibuf() { | ||
if (gzf) { | ||
int err = Zlib::gzclose(gzf); | ||
if (err != Z_OK) { | ||
// OK to overwrite rr it, it doesn't change | ||
const char* error_msg = Zlib::gzerror(gzf, &err); | ||
log_error("%s", error_msg); | ||
} | ||
} | ||
} | ||
|
||
#endif // YOSYS_ENABLE_ZLIB | ||
|
||
|
||
// Takes a successfully opened ifstream. If it's gzipped, returns an istream. Otherwise, | ||
// returns the original ifstream, rewound to the start. | ||
std::istream* uncompressed(const std::string filename, std::ios_base::openmode mode) { | ||
std::ifstream* f = new std::ifstream(); | ||
f->open(filename, mode); | ||
if (f->fail()) | ||
return f; | ||
// Check for gzip magic | ||
unsigned char magic[3]; | ||
int n = 0; | ||
while (n < 3) | ||
{ | ||
int c = f->get(); | ||
if (c != EOF) { | ||
magic[n] = (unsigned char) c; | ||
} | ||
n++; | ||
} | ||
if (n == 3 && magic[0] == 0x1f && magic[1] == 0x8b) { | ||
#ifdef YOSYS_ENABLE_ZLIB | ||
log("Found gzip magic in file `%s', decompressing using zlib.\n", filename.c_str()); | ||
if (magic[2] != 8) | ||
log_cmd_error("gzip file `%s' uses unsupported compression type %02x\n", | ||
filename.c_str(), unsigned(magic[2])); | ||
gzip_istream* s = new gzip_istream(); | ||
delete f; | ||
s->open(filename.c_str()); | ||
return s; | ||
#else | ||
log_cmd_error("File `%s' is a gzip file, but Yosys is compiled without zlib.\n", filename.c_str()); | ||
#endif // YOSYS_ENABLE_ZLIB | ||
} else { | ||
f->clear(); | ||
f->seekg(0, std::ios::beg); | ||
return f; | ||
} | ||
} | ||
|
||
YOSYS_NAMESPACE_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
#include <string> | ||
#include "kernel/yosys_common.h" | ||
|
||
#ifndef YOSYS_GZIP_H | ||
#define YOSYS_GZIP_H | ||
|
||
YOSYS_NAMESPACE_BEGIN | ||
|
||
#ifdef YOSYS_ENABLE_ZLIB | ||
|
||
namespace Zlib { | ||
#include <zlib.h> | ||
} | ||
|
||
/* | ||
An output stream that uses a stringbuf to buffer data internally, | ||
using zlib to write gzip-compressed data every time the stream is flushed. | ||
*/ | ||
class gzip_ostream : public std::ostream { | ||
public: | ||
gzip_ostream(): std::ostream(nullptr) { | ||
rdbuf(&outbuf); | ||
} | ||
bool open(const std::string &filename) { | ||
return outbuf.open(filename); | ||
} | ||
private: | ||
class obuf : public std::stringbuf { | ||
public: | ||
obuf(); | ||
bool open(const std::string &filename); | ||
virtual int sync() override; | ||
virtual ~obuf(); | ||
private: | ||
static const int buffer_size = 4096; | ||
char buffer[buffer_size]; // Internal buffer for compressed data | ||
Zlib::gzFile gzf = nullptr; // Handle to the gzip file | ||
}; | ||
|
||
obuf outbuf; // The stream buffer instance | ||
}; | ||
|
||
/* | ||
An input stream that uses zlib to read gzip-compressed data from a file, | ||
buffering the decompressed data internally using its own buffer. | ||
*/ | ||
class gzip_istream final : public std::istream { | ||
public: | ||
gzip_istream() : std::istream(&inbuf) {} | ||
bool open(const std::string& filename) { | ||
return inbuf.open(filename); | ||
} | ||
private: | ||
class ibuf final : public std::streambuf { | ||
public: | ||
ibuf() : gzf(nullptr) {} | ||
bool open(const std::string& filename); | ||
virtual ~ibuf(); | ||
|
||
protected: | ||
// Called when the buffer is empty and more input is needed | ||
virtual int_type underflow() override; | ||
private: | ||
static const int buffer_size = 8192; | ||
char buffer[buffer_size]; | ||
Zlib::gzFile gzf; | ||
}; | ||
|
||
ibuf inbuf; // The stream buffer instance | ||
}; | ||
|
||
#endif // YOSYS_ENABLE_ZLIB | ||
|
||
std::istream* uncompressed(const std::string filename, std::ios_base::openmode mode = std::ios_base::in); | ||
|
||
YOSYS_NAMESPACE_END | ||
|
||
#endif // YOSYS_GZIP_H |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading https://en.cppreference.com/w/cpp/io/basic_streambuf/underflow we might need to do something about "On failure, the function ensures that either gptr() == nullptr or gptr() == egptr." because the invariant of when this function is called is weaker, it is "The public functions of std::streambuf call this function only if gptr() == nullptr or gptr() >= egptr()."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, now I
setg(eback(), egptr(), egptr());
before returning eof