Remove std::sstream from filamat (#1176)

This commit is contained in:
Ben Doherty
2019-05-13 13:56:15 -07:00
committed by GitHub
parent 4237ff42c9
commit daddc25de8
13 changed files with 701 additions and 377 deletions

View File

@@ -6,6 +6,7 @@ A new header is inserted each time a *tag* is created.
# Release notes
- Further reduced `filamat` binary size by removing reliance on stdlib.
- Added a new, smaller, version of the `filamat` library, `filamat_lite`. Material optimization and
compiling for non-OpenGL backends have been removed in favor of a smaller binary size.
- Implemented hard fences for the Metal backend, enablying dynamic resolution support.

View File

@@ -28,12 +28,12 @@ using namespace filament;
using namespace backend;
using namespace utils;
std::ostream& CodeGenerator::generateSeparator(std::ostream& out) const {
io::sstream& CodeGenerator::generateSeparator(io::sstream& out) const {
out << '\n';
return out;
}
std::ostream& CodeGenerator::generateProlog(std::ostream& out, ShaderType type,
io::sstream& CodeGenerator::generateProlog(io::sstream& out, ShaderType type,
bool hasExternalSamplers) const {
assert(mShaderModel != ShaderModel::UNKNOWN);
switch (mShaderModel) {
@@ -111,12 +111,12 @@ Precision CodeGenerator::getDefaultUniformPrecision() const {
}
}
std::ostream& CodeGenerator::generateEpilog(std::ostream& out) const {
io::sstream& CodeGenerator::generateEpilog(io::sstream& out) const {
out << "\n"; // For line compression all shaders finish with a newline character.
return out;
}
std::ostream& CodeGenerator::generateShaderMain(std::ostream& out, ShaderType type) const {
io::sstream& CodeGenerator::generateShaderMain(io::sstream& out, ShaderType type) const {
if (type == ShaderType::VERTEX) {
out << SHADERS_SHADOWING_VS_DATA;
out << SHADERS_MAIN_VS_DATA;
@@ -126,7 +126,7 @@ std::ostream& CodeGenerator::generateShaderMain(std::ostream& out, ShaderType ty
return out;
}
std::ostream& CodeGenerator::generatePostProcessMain(std::ostream& out,
io::sstream& CodeGenerator::generatePostProcessMain(io::sstream& out,
ShaderType type, filament::PostProcessStage variant) const {
if (type == ShaderType::VERTEX) {
out << SHADERS_POST_PROCESS_VS_DATA;
@@ -148,7 +148,7 @@ std::ostream& CodeGenerator::generatePostProcessMain(std::ostream& out,
return out;
}
std::ostream& CodeGenerator::generateVariable(std::ostream& out, ShaderType type,
io::sstream& CodeGenerator::generateVariable(io::sstream& out, ShaderType type,
const CString& name, size_t index) const {
if (!name.empty()) {
@@ -163,7 +163,7 @@ std::ostream& CodeGenerator::generateVariable(std::ostream& out, ShaderType type
return out;
}
std::ostream& CodeGenerator::generateShaderInputs(std::ostream& out, ShaderType type,
io::sstream& CodeGenerator::generateShaderInputs(io::sstream& out, ShaderType type,
const AttributeBitset& attributes, Interpolation interpolation) const {
const char* shading = getInterpolationQualifier(interpolation);
@@ -216,7 +216,7 @@ std::ostream& CodeGenerator::generateShaderInputs(std::ostream& out, ShaderType
return out;
}
std::ostream& CodeGenerator::generateDepthShaderMain(std::ostream& out, ShaderType type) const {
io::sstream& CodeGenerator::generateDepthShaderMain(io::sstream& out, ShaderType type) const {
if (type == ShaderType::VERTEX) {
out << SHADERS_DEPTH_MAIN_VS_DATA;
} else if (type == ShaderType::FRAGMENT) {
@@ -236,7 +236,7 @@ const char* CodeGenerator::getUniformPrecisionQualifier(UniformType type, Precis
return getPrecisionQualifier(precision, defaultPrecision);
}
std::ostream& CodeGenerator::generateUniforms(std::ostream& out, ShaderType shaderType,
io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderType shaderType,
uint8_t binding, const UniformInterfaceBlock& uib) const {
auto const& infos = uib.getUniformInfoList();
if (infos.empty()) {
@@ -273,8 +273,8 @@ std::ostream& CodeGenerator::generateUniforms(std::ostream& out, ShaderType shad
return out;
}
std::ostream& CodeGenerator::generateSamplers(
std::ostream& out, uint8_t firstBinding, const SamplerInterfaceBlock& sib) const {
io::sstream& CodeGenerator::generateSamplers(
io::sstream& out, uint8_t firstBinding, const SamplerInterfaceBlock& sib) const {
auto const& infos = sib.getSamplerInfoList();
if (infos.empty()) {
return out;
@@ -362,29 +362,31 @@ void CodeGenerator::fixupExternalSamplers(
}
std::ostream& CodeGenerator::generateDefine(std::ostream& out, const char* name, bool value) const {
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, bool value) const {
if (value) {
out << "#define " << name << "\n";
}
return out;
}
std::ostream& CodeGenerator::generateDefine(std::ostream& out, const char* name, float value) const {
out << "#define " << name << " " << std::fixed << std::setprecision(1) << value << "\n";
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, float value) const {
char buffer[32];
snprintf(buffer, 32, "%.1f", value);
out << "#define " << name << " " << buffer << "\n";
return out;
}
std::ostream& CodeGenerator::generateDefine(std::ostream& out, const char* name, uint32_t value) const {
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, uint32_t value) const {
out << "#define " << name << " " << value << "\n";
return out;
}
std::ostream& CodeGenerator::generateDefine(std::ostream& out, const char* name, const char* string) const {
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, const char* string) const {
out << "#define " << name << " " << string << "\n";
return out;
}
std::ostream& CodeGenerator::generateFunction(std::ostream& out, const char* returnType,
io::sstream& CodeGenerator::generateFunction(io::sstream& out, const char* returnType,
const char* name, const char* body) const {
out << "\n" << returnType << " " << name << "()";
out << " {\n" << body;
@@ -392,7 +394,7 @@ std::ostream& CodeGenerator::generateFunction(std::ostream& out, const char* ret
return out;
}
std::ostream& CodeGenerator::generateMaterialProperty(std::ostream& out,
io::sstream& CodeGenerator::generateMaterialProperty(io::sstream& out,
MaterialBuilder::Property property, bool isSet) const {
if (isSet) {
out << "#define " << "MATERIAL_HAS_" << getConstantName(property) << "\n";
@@ -400,7 +402,7 @@ std::ostream& CodeGenerator::generateMaterialProperty(std::ostream& out,
return out;
}
std::ostream& CodeGenerator::generateCommon(std::ostream& out, ShaderType type) const {
io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderType type) const {
out << SHADERS_COMMON_MATH_FS_DATA;
if (type == ShaderType::VERTEX) {
} else if (type == ShaderType::FRAGMENT) {
@@ -409,7 +411,7 @@ std::ostream& CodeGenerator::generateCommon(std::ostream& out, ShaderType type)
return out;
}
std::ostream& CodeGenerator::generateCommonMaterial(std::ostream& out, ShaderType type) const {
io::sstream& CodeGenerator::generateCommonMaterial(io::sstream& out, ShaderType type) const {
if (type == ShaderType::VERTEX) {
out << SHADERS_COMMON_MATERIAL_VS_DATA;
} else if (type == ShaderType::FRAGMENT) {
@@ -418,7 +420,7 @@ std::ostream& CodeGenerator::generateCommonMaterial(std::ostream& out, ShaderTyp
return out;
}
std::ostream& CodeGenerator::generateGetters(std::ostream& out, ShaderType type) const {
io::sstream& CodeGenerator::generateGetters(io::sstream& out, ShaderType type) const {
out << SHADERS_COMMON_GETTERS_FS_DATA;
if (type == ShaderType::VERTEX) {
out << SHADERS_GETTERS_VS_DATA;
@@ -428,7 +430,7 @@ std::ostream& CodeGenerator::generateGetters(std::ostream& out, ShaderType type)
return out;
}
std::ostream& CodeGenerator::generateParameters(std::ostream& out, ShaderType type) const {
io::sstream& CodeGenerator::generateParameters(io::sstream& out, ShaderType type) const {
if (type == ShaderType::VERTEX) {
} else if (type == ShaderType::FRAGMENT) {
out << SHADERS_SHADING_PARAMETERS_FS_DATA;
@@ -436,7 +438,7 @@ std::ostream& CodeGenerator::generateParameters(std::ostream& out, ShaderType ty
return out;
}
std::ostream& CodeGenerator::generateShaderLit(std::ostream& out, ShaderType type,
io::sstream& CodeGenerator::generateShaderLit(io::sstream& out, ShaderType type,
filament::Variant variant, filament::Shading shading) const {
if (type == ShaderType::VERTEX) {
} else if (type == ShaderType::FRAGMENT) {
@@ -477,7 +479,7 @@ std::ostream& CodeGenerator::generateShaderLit(std::ostream& out, ShaderType typ
return out;
}
std::ostream& CodeGenerator::generateShaderUnlit(std::ostream& out, ShaderType type,
io::sstream& CodeGenerator::generateShaderUnlit(io::sstream& out, ShaderType type,
filament::Variant variant, bool hasShadowMultiplier) const {
if (type == ShaderType::VERTEX) {
} else if (type == ShaderType::FRAGMENT) {

View File

@@ -31,6 +31,8 @@
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <utils/sstream.h>
#include <private/filament/Variant.h>
namespace filamat {
@@ -52,63 +54,63 @@ public:
filament::backend::ShaderModel getShaderModel() const noexcept { return mShaderModel; }
// insert a separator (can be a new line)
std::ostream& generateSeparator(std::ostream& out) const;
utils::io::sstream& generateSeparator(utils::io::sstream& out) const;
// generate prolog for the given shader
std::ostream& generateProlog(std::ostream& out, ShaderType type, bool hasExternalSamplers) const;
utils::io::sstream& generateProlog(utils::io::sstream& out, ShaderType type, bool hasExternalSamplers) const;
std::ostream& generateEpilog(std::ostream& out) const;
utils::io::sstream& generateEpilog(utils::io::sstream& out) const;
// generate common functions for the given shader
std::ostream& generateCommon(std::ostream& out, ShaderType type) const;
std::ostream& generateCommonMaterial(std::ostream& out, ShaderType type) const;
utils::io::sstream& generateCommon(utils::io::sstream& out, ShaderType type) const;
utils::io::sstream& generateCommonMaterial(utils::io::sstream& out, ShaderType type) const;
// generate the shader's main()
std::ostream& generateShaderMain(std::ostream& out, ShaderType type) const;
std::ostream& generatePostProcessMain(std::ostream& out, ShaderType type,
utils::io::sstream& generateShaderMain(utils::io::sstream& out, ShaderType type) const;
utils::io::sstream& generatePostProcessMain(utils::io::sstream& out, ShaderType type,
filament::PostProcessStage variant) const;
// generate the shader's code for the lit shading model
std::ostream& generateShaderLit(std::ostream& out, ShaderType type,
utils::io::sstream& generateShaderLit(utils::io::sstream& out, ShaderType type,
filament::Variant variant, filament::Shading shading) const;
// generate the shader's code for the unlit shading model
std::ostream& generateShaderUnlit(std::ostream& out, ShaderType type,
utils::io::sstream& generateShaderUnlit(utils::io::sstream& out, ShaderType type,
filament::Variant variant, bool hasShadowMultiplier) const;
// generate declarations for custom interpolants
std::ostream& generateVariable(std::ostream& out, ShaderType type,
utils::io::sstream& generateVariable(utils::io::sstream& out, ShaderType type,
const utils::CString& name, size_t index) const;
// generate declarations for non-custom "in" variables
std::ostream& generateShaderInputs(std::ostream& out, ShaderType type,
utils::io::sstream& generateShaderInputs(utils::io::sstream& out, ShaderType type,
const filament::AttributeBitset& attributes, filament::Interpolation interpolation) const;
// generate no-op shader for depth prepass
std::ostream& generateDepthShaderMain(std::ostream& out, ShaderType type) const;
utils::io::sstream& generateDepthShaderMain(utils::io::sstream& out, ShaderType type) const;
// generate uniforms
std::ostream& generateUniforms(std::ostream& out, ShaderType type, uint8_t binding,
utils::io::sstream& generateUniforms(utils::io::sstream& out, ShaderType type, uint8_t binding,
const filament::UniformInterfaceBlock& uib) const;
// generate samplers
std::ostream& generateSamplers(
std::ostream& out, uint8_t firstBinding, const filament::SamplerInterfaceBlock& sib) const;
utils::io::sstream& generateSamplers(
utils::io::sstream& out, uint8_t firstBinding, const filament::SamplerInterfaceBlock& sib) const;
// generate material properties getters
std::ostream& generateMaterialProperty(std::ostream& out,
utils::io::sstream& generateMaterialProperty(utils::io::sstream& out,
MaterialBuilder::Property property, bool isSet) const;
std::ostream& generateFunction(std::ostream& out,
utils::io::sstream& generateFunction(utils::io::sstream& out,
const char* returnType, const char* name, const char* body) const;
std::ostream& generateDefine(std::ostream& out, const char* name, bool value) const;
std::ostream& generateDefine(std::ostream& out, const char* name, float value) const;
std::ostream& generateDefine(std::ostream& out, const char* name, uint32_t value) const;
std::ostream& generateDefine(std::ostream& out, const char* name, const char* string) const;
utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, bool value) const;
utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, float value) const;
utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, uint32_t value) const;
utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, const char* string) const;
std::ostream& generateGetters(std::ostream& out, ShaderType type) const;
std::ostream& generateParameters(std::ostream& out, ShaderType type) const;
utils::io::sstream& generateGetters(utils::io::sstream& out, ShaderType type) const;
utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderType type) const;
static void fixupExternalSamplers(
std::string& shader, filament::SamplerInterfaceBlock const& sib) noexcept;

View File

@@ -16,8 +16,6 @@
#include "ShaderGenerator.h"
#include <sstream>
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <filament/MaterialEnums.h>
@@ -46,14 +44,14 @@ static const char* getShadingDefine(filament::Shading shading) noexcept {
}
}
static void generateMaterialDefines(std::ostream& os, const CodeGenerator& cg,
static void generateMaterialDefines(utils::io::sstream& os, const CodeGenerator& cg,
MaterialBuilder::PropertyList const properties) noexcept {
for (size_t i = 0; i < MaterialBuilder::MATERIAL_PROPERTIES_COUNT; i++) {
cg.generateMaterialProperty(os, static_cast<MaterialBuilder::Property>(i), properties[i]);
}
}
static void generateVertexDomain(const CodeGenerator& cg, std::stringstream& vs,
static void generateVertexDomain(const CodeGenerator& cg, utils::io::sstream& vs,
filament::VertexDomain domain) noexcept {
switch (domain) {
case VertexDomain::OBJECT:
@@ -71,11 +69,11 @@ static void generateVertexDomain(const CodeGenerator& cg, std::stringstream& vs,
}
}
static size_t countLines(const std::stringstream& ss) noexcept {
std::string s = ss.str();
static size_t countLines(const char* s) noexcept {
size_t lines = 0;
for (char i : s) {
if (i == '\n') lines++;
size_t i = 0;
while (s[i] != 0) {
if (s[i++] == '\n') lines++;
}
return lines;
}
@@ -88,10 +86,10 @@ static size_t countLines(const utils::CString& s) noexcept {
return lines;
}
static void appendShader(std::stringstream& ss,
static void appendShader(utils::io::sstream& ss,
const utils::CString& shader, size_t lineOffset) noexcept {
if (!shader.empty()) {
size_t lines = countLines(ss);
size_t lines = countLines(ss.c_str());
ss << "#line " << lineOffset;
if (shader[0] != '\n') ss << "\n";
ss << shader.c_str();
@@ -132,7 +130,7 @@ const std::string ShaderGenerator::createVertexProgram(filament::backend::Shader
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, uint8_t variantKey, filament::Interpolation interpolation,
filament::VertexDomain vertexDomain) const noexcept {
std::stringstream vs;
utils::io::sstream vs;
const CodeGenerator cg(shaderModel, targetApi, targetLanguage);
const bool lit = material.isLit;
@@ -204,7 +202,7 @@ const std::string ShaderGenerator::createVertexProgram(filament::backend::Shader
cg.generateEpilog(vs);
return vs.str();
return vs.c_str();
}
bool ShaderGenerator::hasCustomDepthShader() const noexcept {
@@ -225,7 +223,7 @@ const std::string ShaderGenerator::createFragmentProgram(filament::backend::Shad
const bool lit = material.isLit;
const filament::Variant variant(variantKey);
std::stringstream fs;
utils::io::sstream fs;
cg.generateProlog(fs, ShaderType::FRAGMENT, material.hasExternalSamplers);
cg.generateDefine(fs, "IBL_USE_RGBM", filament::CONFIG_IBL_RGBM);
@@ -333,7 +331,7 @@ const std::string ShaderGenerator::createFragmentProgram(filament::backend::Shad
cg.generateEpilog(fs);
return fs.str();
return fs.c_str();
}
void ShaderGenerator::fixupExternalSamplers(filament::backend::ShaderModel sm,
@@ -350,7 +348,7 @@ const std::string ShaderPostProcessGenerator::createPostProcessVertexProgram(
MaterialBuilder::TargetLanguage targetLanguage, filament::PostProcessStage variant,
uint8_t firstSampler) noexcept {
const CodeGenerator cg(sm, targetApi, targetLanguage);
std::stringstream vs;
utils::io::sstream vs;
cg.generateProlog(vs, ShaderType::VERTEX, false);
cg.generateDefine(vs, "LOCATION_POSITION", uint32_t(VertexAttribute::POSITION));
generatePostProcessStageDefines(vs, cg, variant);
@@ -365,7 +363,7 @@ const std::string ShaderPostProcessGenerator::createPostProcessVertexProgram(
cg.generateCommon(vs, ShaderType::VERTEX);
cg.generatePostProcessMain(vs, ShaderType::VERTEX, variant);
cg.generateEpilog(vs);
return vs.str();
return vs.c_str();
}
const std::string ShaderPostProcessGenerator::createPostProcessFragmentProgram(
@@ -373,7 +371,7 @@ const std::string ShaderPostProcessGenerator::createPostProcessFragmentProgram(
MaterialBuilder::TargetLanguage targetLanguage, filament::PostProcessStage variant,
uint8_t firstSampler) noexcept {
const CodeGenerator cg(sm, targetApi, targetLanguage);
std::stringstream fs;
utils::io::sstream fs;
cg.generateProlog(fs, ShaderType::FRAGMENT, false);
generatePostProcessStageDefines(fs, cg, variant);
@@ -387,10 +385,10 @@ const std::string ShaderPostProcessGenerator::createPostProcessFragmentProgram(
cg.generateCommon(fs, ShaderType::FRAGMENT);
cg.generatePostProcessMain(fs, ShaderType::FRAGMENT, variant);
cg.generateEpilog(fs);
return fs.str();
return fs.c_str();
}
void ShaderPostProcessGenerator::generatePostProcessStageDefines(std::stringstream& vs,
void ShaderPostProcessGenerator::generatePostProcessStageDefines(utils::io::sstream& vs,
CodeGenerator const& cg, PostProcessStage variant) noexcept {
cg.generateDefine(vs, "POST_PROCESS_TONE_MAPPING_OPAQUE",
uint32_t(PostProcessStage::TONE_MAPPING_OPAQUE));

View File

@@ -26,6 +26,7 @@
#include "MaterialInfo.h"
#include <utils/CString.h>
#include <utils/sstream.h>
namespace filamat {
@@ -79,7 +80,7 @@ struct ShaderPostProcessGenerator {
static const std::string createPostProcessFragmentProgram(filament::backend::ShaderModel sm,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
filament::PostProcessStage variant, uint8_t firstSampler) noexcept;
static void generatePostProcessStageDefines(std::stringstream& vs, CodeGenerator const& cg,
static void generatePostProcessStageDefines(utils::io::sstream& vs, CodeGenerator const& cg,
filament::PostProcessStage variant) noexcept;
};

View File

@@ -33,9 +33,11 @@ set(SRCS
src/JobSystem.cpp
src/Log.cpp
src/NameComponentManager.cpp
src/ostream.cpp
src/Panic.cpp
src/Path.cpp
src/Profiler.cpp
src/sstream.cpp
src/Systrace.cpp
)
if (WIN32)
@@ -105,6 +107,7 @@ set(TEST_SRCS
test/test_Entity.cpp
test/test_JobSystem.cpp
test/test_StructureOfArrays.cpp
test/test_sstream.cpp
test/test_utils_main.cpp
test/test_Zip2Iterator.cpp
test/test_BinaryTreeArray.cpp

View File

@@ -23,141 +23,40 @@
#include <utils/bitset.h>
#include <utils/compiler.h> // ssize_t is a POSIX type.
// We always use TINY IO, it seems to work well enough and that a nice way to not
// break the android builds
#define UTILS_TINY_IO 1
#ifndef UTILS_TINY_IO
# ifdef ANDROID
# define UTILS_TINY_IO 1
# else
# define UTILS_TINY_IO 0
# endif
#endif
#if !UTILS_TINY_IO
#include <ios>
#include <ostream>
#endif
#include <utils/ostream.h>
namespace utils {
namespace io {
#if UTILS_TINY_IO
class ostream {
class LogStream : public ostream {
public:
enum Priority {
DEBUG, ERROR, WARNING, INFO
};
explicit ostream(Priority p) noexcept : mPriority(p) { }
ostream& operator<<(short value) noexcept;
ostream& operator<<(unsigned short value) noexcept;
ostream& operator<<(int value) noexcept;
ostream& operator<<(unsigned int value) noexcept;
ostream& operator<<(long value) noexcept;
ostream& operator<<(unsigned long value) noexcept;
ostream& operator<<(long long value) noexcept;
ostream& operator<<(unsigned long long value) noexcept;
ostream& operator<<(float value) noexcept;
ostream& operator<<(double value) noexcept;
ostream& operator<<(long double value) noexcept;
ostream& operator<<(bool value) noexcept;
ostream& operator<<(const void* value) noexcept;
ostream& operator<<(const char* string) noexcept;
ostream& operator<<(ostream& (* f)(ostream&)) noexcept { return f(*this); }
explicit LogStream(Priority p) noexcept : mPriority(p) {}
ostream& flush() noexcept override;
private:
ostream& dec() noexcept;
ostream& hex() noexcept;
ostream& flush() noexcept;
friend ostream& hex(ostream& s) noexcept;
friend ostream& dec(ostream& s) noexcept;
friend ostream& endl(ostream& s) noexcept;
friend ostream& flush(ostream& s) noexcept;
enum type {
SHORT, USHORT, INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, DOUBLE, LONG_DOUBLE
};
bool mShowHex = false;
Priority mPriority;
const char* getFormat(type t) const noexcept;
class Buffer {
char mStorage[16384];
public:
char* curr = mStorage;
size_t size = sizeof(mStorage);
const char* get() const noexcept { return mStorage; }
void advance(ssize_t n) noexcept;
void reset() noexcept;
};
Buffer& getBuffer() noexcept { return mData; }
static UTILS_DECLARE_TLS(Buffer) mData;
};
// handles std::string
inline ostream& operator << (ostream& o, std::string const& s) noexcept { return o << s.c_str(); }
// handles utils::bitset
inline ostream& operator << (ostream& o, utils::bitset32 const& s) noexcept {
return o << (void*)uintptr_t(s.getValue());
}
// handles vectors from libmath (but we do this generically, without needing a dependency on libmath)
template<template<typename T> class VECTOR, typename T>
inline ostream& operator<<(ostream& stream, const VECTOR<T>& v) {
stream << "< ";
for (size_t i = 0; i < v.size() - 1; i++) {
stream << T(v[i]) << ", ";
}
stream << T(v[v.size() - 1]) << " >";
return stream;
}
inline ostream& hex(ostream& s) noexcept { return s.hex(); }
inline ostream& dec(ostream& s) noexcept { return s.dec(); }
inline ostream& endl(ostream& s) noexcept { s << "\n"; return s.flush(); }
inline ostream& flush(ostream& s) noexcept { return s.flush(); }
#else // UTILS_TINY_IO
using ostream = std::ostream;
inline ostream& hex(ostream& s) noexcept { return s << std::hex; }
inline ostream& dec(ostream& s) noexcept { return s << std::dec; }
inline ostream& endl(ostream& s) noexcept { return s << std::endl; }
inline ostream& flush(ostream& s) noexcept { return s << std::flush; }
#endif // UTILS_TINY_IO
} // namespace io
struct Loggers {
// DEBUG level logging stream
io::ostream& d;
io::LogStream& d;
// ERROR level logging stream
io::ostream& e;
io::LogStream& e;
// WARNING level logging stream
io::ostream& w;
io::LogStream& w;
// INFORMATION level logging stream
io::ostream& i;
io::LogStream& i;
};
extern Loggers slog;

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed 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 TNT_UTILS_OSTREAM_H
#define TNT_UTILS_OSTREAM_H
#include <string>
#include <utils/bitset.h>
#include <utils/compiler.h> // ssize_t is a POSIX type.
namespace utils {
namespace io {
class ostream {
public:
virtual ~ostream();
ostream& operator<<(short value) noexcept;
ostream& operator<<(unsigned short value) noexcept;
ostream& operator<<(char value) noexcept;
ostream& operator<<(unsigned char value) noexcept;
ostream& operator<<(int value) noexcept;
ostream& operator<<(unsigned int value) noexcept;
ostream& operator<<(long value) noexcept;
ostream& operator<<(unsigned long value) noexcept;
ostream& operator<<(long long value) noexcept;
ostream& operator<<(unsigned long long value) noexcept;
ostream& operator<<(float value) noexcept;
ostream& operator<<(double value) noexcept;
ostream& operator<<(long double value) noexcept;
ostream& operator<<(bool value) noexcept;
ostream& operator<<(const void* value) noexcept;
ostream& operator<<(const char* string) noexcept;
ostream& operator<<(const unsigned char* string) noexcept;
ostream& operator<<(ostream& (* f)(ostream&)) noexcept { return f(*this); }
ostream& dec() noexcept;
ostream& hex() noexcept;
protected:
class Buffer {
public:
Buffer() noexcept;
~Buffer() noexcept;
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
char* buffer;
char* curr;
size_t size = 0;
size_t capacity = 0;
const char* get() const noexcept { return buffer; }
void advance(ssize_t n) noexcept;
void reset() noexcept;
void resize(size_t newSize) noexcept;
};
Buffer mData;
Buffer& getBuffer() noexcept { return mData; }
private:
virtual ostream& flush() noexcept = 0;
friend ostream& hex(ostream& s) noexcept;
friend ostream& dec(ostream& s) noexcept;
friend ostream& endl(ostream& s) noexcept;
friend ostream& flush(ostream& s) noexcept;
enum type {
SHORT, USHORT, CHAR, UCHAR, INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, DOUBLE,
LONG_DOUBLE
};
bool mShowHex = false;
const char* getFormat(type t) const noexcept;
/*
* Checks that the buffer has room for s additional bytes, growing the allocation if necessary.
*/
void growBufferIfNeeded(size_t s) noexcept;
};
// handles std::string
inline ostream& operator << (ostream& o, std::string const& s) noexcept { return o << s.c_str(); }
// handles utils::bitset
inline ostream& operator << (ostream& o, utils::bitset32 const& s) noexcept {
return o << (void*)uintptr_t(s.getValue());
}
// handles vectors from libmath (but we do this generically, without needing a dependency on libmath)
template<template<typename T> class VECTOR, typename T>
inline ostream& operator<<(ostream& stream, const VECTOR<T>& v) {
stream << "< ";
for (size_t i = 0; i < v.size() - 1; i++) {
stream << T(v[i]) << ", ";
}
stream << T(v[v.size() - 1]) << " >";
return stream;
}
inline ostream& hex(ostream& s) noexcept { return s.hex(); }
inline ostream& dec(ostream& s) noexcept { return s.dec(); }
inline ostream& endl(ostream& s) noexcept { s << "\n"; return s.flush(); }
inline ostream& flush(ostream& s) noexcept { return s.flush(); }
} // namespace io
} // namespace utils
#endif // TNT_UTILS_OSTREAM_H

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed 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 TNT_SSTREAM_H
#define TNT_SSTREAM_H
#include <utils/ostream.h>
namespace utils {
namespace io {
class sstream : public ostream {
public:
ostream &flush() noexcept override;
const char* c_str() const noexcept;
};
} // namespace io
} // namespace utils
#endif //TNT_SSTREAM_H

View File

@@ -26,129 +26,11 @@
# endif
#endif
#if (!UTILS_TINY_IO)
# include <iostream>
#endif
namespace utils {
#if (UTILS_TINY_IO)
namespace io {
UTILS_DEFINE_TLS(ostream::Buffer) ostream::mData;
UTILS_NOINLINE
void ostream::Buffer::advance(ssize_t n) noexcept {
if (n > 0) {
size_t written = n < size ? size_t(n) : size;
curr += written;
size -= written;
}
}
UTILS_NOINLINE
void ostream::Buffer::reset() noexcept {
curr = mStorage;
size = sizeof(mStorage);
}
const char* ostream::getFormat(ostream::type t) const noexcept {
switch (t) {
case type::SHORT: return mShowHex ? "0x%hx" : "%hd";
case type::USHORT: return mShowHex ? "0x%hx" : "%hu";
case type::INT: return mShowHex ? "0x%x" : "%d";
case type::UINT: return mShowHex ? "0x%x" : "%u";
case type::LONG: return mShowHex ? "0x%lx" : "%ld";
case type::ULONG: return mShowHex ? "0x%lx" : "%lu";
case type::LONG_LONG: return mShowHex ? "0x%llx" : "%lld";
case type::ULONG_LONG: return mShowHex ? "0x%llx" : "%llu";
case type::DOUBLE: return "%f";
case type::LONG_DOUBLE: return "%Lf";
}
}
ostream& ostream::operator<<(short value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::SHORT), value));
return *this;
}
ostream& ostream::operator<<(unsigned short value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::USHORT), value));
return *this;
}
ostream& ostream::operator<<(int value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::INT), value));
return *this;
}
ostream& ostream::operator<<(unsigned int value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::UINT), value));
return *this;
}
ostream& ostream::operator<<(long value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG), value));
return *this;
}
ostream& ostream::operator<<(unsigned long value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::ULONG), value));
return *this;
}
ostream& ostream::operator<<(long long value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG_LONG), value));
return *this;
}
ostream& ostream::operator<<(unsigned long long value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::ULONG_LONG), value));
return *this;
}
ostream& ostream::operator<<(float value) noexcept {
return operator<<((double)value);
}
ostream& ostream::operator<<(double value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::DOUBLE), value));
return *this;
}
ostream& ostream::operator<<(long double value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG_DOUBLE), value));
return *this;
}
ostream& ostream::operator<<(bool value) noexcept {
return operator<<((int)value);
}
ostream& ostream::operator<<(const char* string) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, "%s", string));
return *this;
}
ostream& ostream::operator<<(const void* value) noexcept {
Buffer& buf = getBuffer();
buf.advance(snprintf(buf.curr, buf.size, "%p", value));
return *this;
}
ostream& ostream::flush() noexcept {
ostream& LogStream::flush() noexcept {
Buffer& buf = getBuffer();
#if ANDROID
switch (mPriority) {
@@ -181,20 +63,10 @@ ostream& ostream::flush() noexcept {
return *this;
}
ostream& ostream::hex() noexcept {
mShowHex = true;
return *this;
}
ostream& ostream::dec() noexcept {
mShowHex = false;
return *this;
}
static ostream cout(ostream::Priority::DEBUG);
static ostream cerr(ostream::Priority::ERROR);
static ostream cwarn(ostream::Priority::WARNING);
static ostream cinfo(ostream::Priority::INFO);
static LogStream cout(LogStream::Priority::DEBUG);
static LogStream cerr(LogStream::Priority::ERROR);
static LogStream cwarn(LogStream::Priority::WARNING);
static LogStream cinfo(LogStream::Priority::INFO);
} // namespace io
@@ -206,71 +78,4 @@ Loggers slog = {
io::cinfo // info
};
#else // UTILS_TINY_IO
#if ANDROID
static constexpr const size_t LOG_BUFFER_SIZE = 1024;
class AndroidStreamBuffer : public std::streambuf {
public:
explicit AndroidStreamBuffer(android_LogPriority priority) {
mPriority = priority;
setp(mBuffer, mBuffer + LOG_BUFFER_SIZE - 1);
}
private:
int overflow(int c) {
if (c != traits_type::eof()) {
*pptr() = traits_type::to_char_type(c);
pbump(1);
if (sync() >= 0) {
return c;
}
}
return traits_type::eof();
}
int sync() {
if (pbase() != pptr()) {
char buffer[LOG_BUFFER_SIZE + 1];
std::ptrdiff_t length = pptr() - pbase();
memcpy(buffer, pbase(), length);
buffer[length] = '\0';
pbump(-length);
__android_log_write(mPriority, UTILS_LOG_TAG, buffer);
}
return 0;
}
android_LogPriority mPriority;
char mBuffer[LOG_BUFFER_SIZE];
};
static std::ostream android_cout(new AndroidStreamBuffer(ANDROID_LOG_DEBUG));
static std::ostream android_cerr(new AndroidStreamBuffer(ANDROID_LOG_ERROR));
static std::ostream android_cwarn(new AndroidStreamBuffer(ANDROID_LOG_WARN));
static std::ostream android_cinfo(new AndroidStreamBuffer(ANDROID_LOG_INFO));
Loggers slog = {
android_cout, // debug
android_cerr, // error
android_cwarn, // warning
android_cinfo // info
};
#else // ANDROID
Loggers slog = {
std::cout, // debug
std::cerr, // error
std::cout, // warning
std::cout // info
};
#endif
#endif // UTILS_TINY_IO
} // namespace utils

240
libs/utils/src/ostream.cpp Normal file
View File

@@ -0,0 +1,240 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed 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.
*/
#include <utils/ostream.h>
#include <string>
#include <utils/compiler.h>
#include <iostream>
#include <utils/trap.h>
namespace utils {
namespace io {
ostream::~ostream() = default;
ostream::Buffer::Buffer() noexcept {
constexpr size_t initialSize = 1024;
buffer = (char*) malloc(initialSize);
assert(buffer);
curr = buffer;
capacity = initialSize;
size = initialSize;
}
ostream::Buffer::~Buffer() noexcept {
free(buffer);
}
UTILS_NOINLINE
void ostream::Buffer::advance(ssize_t n) noexcept {
if (n > 0) {
size_t written = n < size ? size_t(n) : size;
curr += written;
size -= written;
}
}
UTILS_NOINLINE
void ostream::Buffer::resize(size_t newSize) noexcept {
size_t offset = curr - buffer;
buffer = (char*) realloc(buffer, newSize);
assert(buffer);
capacity = newSize;
curr = buffer + offset;
size = capacity - offset;
}
UTILS_NOINLINE
void ostream::Buffer::reset() noexcept {
curr = buffer;
size = capacity;
}
const char* ostream::getFormat(ostream::type t) const noexcept {
switch (t) {
case type::SHORT: return mShowHex ? "0x%hx" : "%hd";
case type::USHORT: return mShowHex ? "0x%hx" : "%hu";
case type::CHAR: return mShowHex ? "0x%x" : "%c";
case type::UCHAR: return mShowHex ? "0x%x" : "%c";
case type::INT: return mShowHex ? "0x%x" : "%d";
case type::UINT: return mShowHex ? "0x%x" : "%u";
case type::LONG: return mShowHex ? "0x%lx" : "%ld";
case type::ULONG: return mShowHex ? "0x%lx" : "%lu";
case type::LONG_LONG: return mShowHex ? "0x%llx" : "%lld";
case type::ULONG_LONG: return mShowHex ? "0x%llx" : "%llu";
case type::DOUBLE: return "%f";
case type::LONG_DOUBLE: return "%Lf";
}
}
void ostream::growBufferIfNeeded(size_t s) noexcept {
Buffer& buf = getBuffer();
const size_t used = buf.curr - buf.buffer; // space currently used in buffer
if (UTILS_UNLIKELY(buf.size < s)) {
size_t newSize = buf.capacity * 2;
while (UTILS_UNLIKELY((newSize - used) < s)) {
newSize *= 2;
}
buf.resize(newSize);
assert(buf.size >= s);
}
}
ostream& ostream::operator<<(short value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::SHORT), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::SHORT), value));
return *this;
}
ostream& ostream::operator<<(unsigned short value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::USHORT), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::USHORT), value));
return *this;
}
ostream& ostream::operator<<(char value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::CHAR), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::CHAR), value));
return *this;
}
ostream& ostream::operator<<(unsigned char value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::UCHAR), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::UCHAR), value));
return *this;
}
ostream& ostream::operator<<(int value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::INT), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::INT), value));
return *this;
}
ostream& ostream::operator<<(unsigned int value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::UINT), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::UINT), value));
return *this;
}
ostream& ostream::operator<<(long value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::LONG), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG), value));
return *this;
}
ostream& ostream::operator<<(unsigned long value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::ULONG), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::ULONG), value));
return *this;
}
ostream& ostream::operator<<(long long value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::LONG_LONG), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG_LONG), value));
return *this;
}
ostream& ostream::operator<<(unsigned long long value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::ULONG_LONG), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::ULONG_LONG), value));
return *this;
}
ostream& ostream::operator<<(float value) noexcept {
return operator<<((double)value);
}
ostream& ostream::operator<<(double value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::DOUBLE), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::DOUBLE), value));
return *this;
}
ostream& ostream::operator<<(long double value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, getFormat(type::LONG_DOUBLE), value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG_DOUBLE), value));
return *this;
}
ostream& ostream::operator<<(bool value) noexcept {
return operator<<((int)value);
}
ostream& ostream::operator<<(const char* string) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, "%s", string);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, "%s", string));
return *this;
}
ostream& ostream::operator<<(const unsigned char* string) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, "%s", string);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, "%s", string));
return *this;
}
ostream& ostream::operator<<(const void* value) noexcept {
Buffer& buf = getBuffer();
size_t s = snprintf(nullptr, 0, "%p", value);
growBufferIfNeeded(s + 1); // +1 to include the null-terminator
buf.advance(snprintf(buf.curr, buf.size, "%p", value));
return *this;
}
ostream& ostream::hex() noexcept {
mShowHex = true;
return *this;
}
ostream& ostream::dec() noexcept {
mShowHex = false;
return *this;
}
} // namespace io
} // namespace utils

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed 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.
*/
#include <utils/sstream.h>
namespace utils {
namespace io {
utils::io::ostream& sstream::flush() noexcept {
// no-op.
return *this;
}
const char* sstream::c_str() const noexcept {
return mData.get();
}
} // namespace io
} // namespace utils

View File

@@ -0,0 +1,168 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed 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.
*/
#include <gtest/gtest.h>
#include <utils/sstream.h>
using namespace utils::io;
TEST(sstream, EmptyString) {
sstream ss;
EXPECT_STREQ("", ss.c_str());
}
TEST(sstream, Formatting) {
{
sstream ss;
ss << (short) 32767;
EXPECT_STREQ("32767", ss.c_str());
}
{
sstream ss;
ss << (unsigned short) 65535;
EXPECT_STREQ("65535", ss.c_str());
}
{
sstream ss;
ss << (char) 'A';
EXPECT_STREQ("A", ss.c_str());
}
{
sstream ss;
ss << (unsigned char) 'B';
EXPECT_STREQ("B", ss.c_str());
}
{
sstream ss;
ss << (int) 2147483647;
EXPECT_STREQ("2147483647", ss.c_str());
}
{
sstream ss;
ss << (unsigned int) 4294967295;
EXPECT_STREQ("4294967295", ss.c_str());
}
{
sstream ss;
ss << (long) -2147483647;
EXPECT_STREQ("-2147483647", ss.c_str());
}
{
sstream ss;
ss << (unsigned long) 4294967295;
EXPECT_STREQ("4294967295", ss.c_str());
}
{
sstream ss;
ss << (long long) 9223372036854775807;
EXPECT_STREQ("9223372036854775807", ss.c_str());
}
{
sstream ss;
ss << (unsigned long long) 18446744073709551615u;
EXPECT_STREQ("18446744073709551615", ss.c_str());
}
{
sstream ss;
ss << (float) 3.14;
EXPECT_STREQ("3.140000", ss.c_str());
}
{
sstream ss;
ss << (double) -1;
EXPECT_STREQ("-1.000000", ss.c_str());
}
{
sstream ss;
ss << (long double) 1;
EXPECT_STREQ("1.000000", ss.c_str());
}
{
sstream ss;
ss << (bool) true;
EXPECT_STREQ("1", ss.c_str());
}
{
sstream ss;
ss << (const void *) 0x12345678;
EXPECT_STREQ("0x12345678", ss.c_str());
}
{
sstream ss;
ss << (const char *) "hello";
EXPECT_STREQ("hello", ss.c_str());
}
{
sstream ss;
ss << (const unsigned char *) "world";
EXPECT_STREQ("world", ss.c_str());
}
}
TEST(sstream, LargeBuffer) {
sstream ss;
const char* filler = "1234567890ABCDEF";
// Fill the buffer with 1024 * 1024 * 16 bytes (~16MB).
for (size_t i = 0; i < 1024 * 1024; i++) {
ss << filler;
}
EXPECT_EQ(1024 * 1024 * 16, strlen(ss.c_str()));
}
TEST(sstream, LargeString) {
sstream ss;
// Create a 1GB C-string.
constexpr size_t size = 1024 * 1024 * 1024;
char* filler = (char*) malloc(size + 1);
std::fill_n(filler, size, 'A');
filler[size] = 0; // null-terminator
ss << filler;
EXPECT_EQ(size, strlen(ss.c_str()));
EXPECT_STREQ(filler, ss.c_str());
free(filler);
}
TEST(sstream, SeveralStrings) {
sstream ss;
// Create a 1KB C-string.
constexpr size_t sizeA = 1024;
char* fillerA = (char*) malloc(sizeA + 1);
std::fill_n(fillerA, sizeA, 'A');
fillerA[sizeA] = 0; // null-terminator
// Create a 7KB C-string.
constexpr size_t sizeB = 7 * 1024;
char* fillerB = (char*) malloc(sizeB + 1);
std::fill_n(fillerB, sizeB, 'B');
fillerB[sizeB] = 0;
ss << fillerA;
ss << fillerB;
EXPECT_EQ(sizeA + sizeB, strlen(ss.c_str()));
free(fillerA);
free(fillerB);
}