core: safe handling of compressed file level extension (.gz[0-9])

- Replace unsafe pointer arithmetic and direct buffer modification with std::string methods.
- Update documentation to clarify that the last digit is used as compression level and truncated from the actual filename.
- Add test cases for .gz and .gz0-9
This commit is contained in:
Kumataro
2026-05-05 07:41:59 +09:00
parent 5cbfe53798
commit 8449b9e468
3 changed files with 42 additions and 13 deletions

View File

@@ -306,8 +306,12 @@ public:
before opening the file. before opening the file.
@param filename Name of the file to open or the text string to read the data from. @param filename Name of the file to open or the text string to read the data from.
Extension of the file (.xml, .yml/.yaml or .json) determines its format (XML, YAML or JSON Extension of the file (.xml, .yml/.yaml or .json) determines its format (XML, YAML or JSON
respectively). Also you can append .gz to work with compressed files, for example myHugeMatrix.xml.gz. If both respectively). Also you can append .gz to work with compressed files, for example myHugeMatrix.xml.gz.
FileStorage::WRITE and FileStorage::MEMORY flags are specified, source is used just to specify You can also specify a compression level from 0 to 9 by appending it to the extension
(e.g. ".gz0" for no compression, ".gz9" for high compression).
The last digit will be truncated internally to write/read.
(e.g. If "a.xml.gz9" is specified, "a.xml.gz" is used for the actual file name.)
If both FileStorage::WRITE and FileStorage::MEMORY flags are specified, source is used just to specify
the output file format (e.g. mydata.xml, .yml etc.). A file name can also contain parameters. the output file format (e.g. mydata.xml, .yml etc.). A file name can also contain parameters.
You can use this format, "*?base64" (e.g. "file.json?base64" (case sensitive)), as an alternative to You can use this format, "*?base64" (e.g. "file.json?base64" (case sensitive)), as an alternative to
FileStorage::BASE64 flag. FileStorage::BASE64 flag.

View File

@@ -544,18 +544,30 @@ bool FileStorage::Impl::open(const char *filename_or_buf, int _flags, const char
flags = _flags; flags = _flags;
if (!mem_mode) { if (!mem_mode) {
char *dot_pos = strrchr((char *) filename.c_str(), '.'); size_t dot_idx = filename.find_last_of('.');
char compression = '\0'; char compression = '\0';
if (dot_pos && dot_pos[1] == 'g' && dot_pos[2] == 'z' && if (dot_idx != std::string::npos)
(dot_pos[3] == '\0' || (cv_isdigit(dot_pos[3]) && dot_pos[4] == '\0'))) { {
if (append) { std::string ext = filename.substr(dot_idx);
CV_Error(cv::Error::StsNotImplemented, "Appending data to compressed file is not implemented");
// Instead of `ext.starts_with(".gz")
if (ext.size() >= 3 && (ext[1] == 'g') && (ext[2] == 'z') )
{
if (ext.size() == 3)
{
// ".gz"
isGZ = true;
}
else if(ext.size() == 4 && cv_isdigit(ext[3]))
{
// ".gz[0-9]"
isGZ = true;
compression = ext[3];
filename.pop_back(); // Replace `gz[0-9]' to 'gz'.
}
} }
isGZ = true;
compression = dot_pos[3];
if (compression)
dot_pos[3] = '\0';
} }
if (!isGZ) { if (!isGZ) {
@@ -567,6 +579,9 @@ bool FileStorage::Impl::open(const char *filename_or_buf, int _flags, const char
} }
} else { } else {
#if USE_ZLIB #if USE_ZLIB
if (append) {
CV_Error(cv::Error::StsNotImplemented, "Appending data to compressed file is not implemented");
}
char mode[] = {write_mode ? 'w' : 'r', 'b', compression ? compression : '3', '\0'}; char mode[] = {write_mode ? 'w' : 'r', 'b', compression ? compression : '3', '\0'};
gzfile = gzopen(filename.c_str(), mode); gzfile = gzopen(filename.c_str(), mode);
if (!gzfile) if (!gzfile)

View File

@@ -2108,9 +2108,19 @@ T fsWriteRead(const T& expectedValue, const char* ext)
fs_w.release(); fs_w.release();
FileStorage fs_r(fname, FileStorage::READ); FileStorage fs_r(fname, FileStorage::READ);
T value; T value;
fs_r["value"] >> value; fs_r["value"] >> value;
fs_r.release();
// If ext is `.gz[0-9]`, fname on storage will end with `.gz`.
// FileStorage::Impl::open() truncates the last digit internally.
if (isdigit(fname.back()))
{
fname.pop_back();
}
remove(fname.c_str());
return value; return value;
} }
@@ -2143,7 +2153,7 @@ TEST_P(FileStorage_exact_type, long_int)
} }
INSTANTIATE_TEST_CASE_P(Core_InputOutput, INSTANTIATE_TEST_CASE_P(Core_InputOutput,
FileStorage_exact_type, Values(".yml", ".xml", ".json") FileStorage_exact_type, Values(".yml", ".xml", ".json", ".xml.gz", ".xml.gz0", ".xml.gz9")
); );
}} // namespace }} // namespace