blob: 30678c07cc0ae1a901a086b1ebed54d1b255fc78 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
**/
#ifndef QUICKSTEP_UTILITY_MEMSTREAM_HPP_
#define QUICKSTEP_UTILITY_MEMSTREAM_HPP_
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include "utility/Macros.hpp"
#include "utility/UtilityConfig.h"
#include "glog/logging.h"
namespace quickstep {
/** \addtogroup Utility
* @{
*/
/**
* @brief Abstract interface for a FILE* stream backed by an in-memory buffer
* (or a simulation thereof). Intended to simplify testing of I/O
* routines that operate on FILE* streams.
**/
class MemStreamInterface {
public:
/**
* @brief Virtual destructor.
**/
virtual ~MemStreamInterface() {
}
/**
* @return A writable FILE* stream.
**/
virtual FILE* file() = 0;
/**
* @return All the bytes that have been written to the FILE* stream so far as
* a c-string.
**/
virtual const char* str() = 0;
/**
* @brief Reset the stream and buffer managed by this object to a clean,
* empty state, as though it had just been constructed.
**/
virtual void reset() = 0;
protected:
MemStreamInterface() {
}
private:
DISALLOW_COPY_AND_ASSIGN(MemStreamInterface);
};
#ifdef QUICKSTEP_HAVE_OPEN_MEMSTREAM
/**
* @brief MemStream implementation using POSIX.1-2008 open_memstream()
* function.
**/
class MemStreamPosix final : public MemStreamInterface {
public:
MemStreamPosix()
: stream_(nullptr),
filebuf_(nullptr),
filebuf_size_(0) {
stream_ = open_memstream(&filebuf_, &filebuf_size_);
CHECK_NOTNULL(stream_);
}
~MemStreamPosix() final {
CHECK_EQ(0, std::fclose(stream_));
std::free(filebuf_);
}
FILE* file() final {
return stream_;
}
const char* str() final {
CHECK_EQ(0, std::fflush(stream_));
return filebuf_;
}
void reset() final {
CHECK_EQ(0, std::fclose(stream_));
std::free(filebuf_);
filebuf_ = nullptr;
filebuf_size_ = 0;
stream_ = open_memstream(&filebuf_, &filebuf_size_);
CHECK_NOTNULL(stream_);
}
private:
FILE *stream_;
char *filebuf_;
std::size_t filebuf_size_;
DISALLOW_COPY_AND_ASSIGN(MemStreamPosix);
};
typedef MemStreamPosix MemStream;
#else
/**
* @brief MemStream simulated by a standard C tmpfile.
**/
class MemStreamStdio final : public MemStreamInterface {
public:
MemStreamStdio()
: stream_(std::tmpfile()),
buffer_(nullptr) {
CHECK_NOTNULL(stream_);
}
~MemStreamStdio() final {
CHECK_EQ(0, std::fclose(stream_));
if (buffer_ != nullptr) {
std::free(buffer_);
}
}
FILE* file() final {
return stream_;
}
const char* str() final {
if (buffer_ != nullptr) {
std::free(buffer_);
}
CHECK_EQ(0, std::fflush(stream_));
long int filepos = std::ftell(stream_); // NOLINT(runtime/int)
CHECK_GE(filepos, 0);
buffer_ = static_cast<char*>(std::malloc(filepos + 1));
std::rewind(stream_);
std::size_t bytes_read = std::fread(buffer_, 1, filepos, stream_);
CHECK_EQ(filepos, bytes_read);
CHECK_EQ(filepos, std::ftell(stream_));
buffer_[filepos] = '\0';
return buffer_;
}
void reset() final {
CHECK_EQ(0, std::fclose(stream_));
if (buffer_ != nullptr) {
std::free(buffer_);
buffer_ = nullptr;
}
stream_ = std::tmpfile();
CHECK_NOTNULL(stream_);
}
private:
FILE *stream_;
char *buffer_;
DISALLOW_COPY_AND_ASSIGN(MemStreamStdio);
};
typedef MemStreamStdio MemStream;
#endif
/** @} */
} // namespace quickstep
#endif // QUICKSTEP_UTILITY_MEMSTREAM_HPP_