-
Notifications
You must be signed in to change notification settings - Fork 897
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
10
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.
+692
−528
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
7c6d2b4
io: refactor string and file work into new unit
widlarizer 6c55df3
gzip: istream
widlarizer 094ab8c
dfflibmap: allow gzipped liberty files
widlarizer 0b96aa1
gzip: simplify uncompressed interface
widlarizer 6310454
io: remove unused unistd.h to fix windows build
widlarizer d75b6bb
dfflibmap: allow gzipped liberty files
widlarizer b3610c4
io: smooth out non-POSIX function usage across platforms
widlarizer 00071c1
gzip: minor refactor
widlarizer fa8642b
gzip: uphold rules for basic_streambuf::underflow overrides
widlarizer b85dda9
gzip: back to pointers
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