diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d868333725..048da7361e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -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. diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index f831ac7cb5..ba8f8901a8 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -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) { diff --git a/libs/filamat/src/shaders/CodeGenerator.h b/libs/filamat/src/shaders/CodeGenerator.h index 7605c70fb3..21a4ef89db 100644 --- a/libs/filamat/src/shaders/CodeGenerator.h +++ b/libs/filamat/src/shaders/CodeGenerator.h @@ -31,6 +31,8 @@ #include #include +#include + #include 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; diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp index 4cad519909..fdedc5d50d 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.cpp +++ b/libs/filamat/src/shaders/ShaderGenerator.cpp @@ -16,8 +16,6 @@ #include "ShaderGenerator.h" -#include - #include #include #include @@ -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(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)); diff --git a/libs/filamat/src/shaders/ShaderGenerator.h b/libs/filamat/src/shaders/ShaderGenerator.h index aee114b3fa..501fc6e9f3 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.h +++ b/libs/filamat/src/shaders/ShaderGenerator.h @@ -26,6 +26,7 @@ #include "MaterialInfo.h" #include +#include 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; }; diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index 27fb094dd6..32ee971d40 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -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 diff --git a/libs/utils/include/utils/Log.h b/libs/utils/include/utils/Log.h index 8bddedd2ac..3e4e7e263f 100644 --- a/libs/utils/include/utils/Log.h +++ b/libs/utils/include/utils/Log.h @@ -23,141 +23,40 @@ #include #include // 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 -#include -#endif +#include 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 class VECTOR, typename T> -inline ostream& operator<<(ostream& stream, const VECTOR& 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; diff --git a/libs/utils/include/utils/ostream.h b/libs/utils/include/utils/ostream.h new file mode 100644 index 0000000000..7f4beb7cda --- /dev/null +++ b/libs/utils/include/utils/ostream.h @@ -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 + +#include +#include // 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 class VECTOR, typename T> +inline ostream& operator<<(ostream& stream, const VECTOR& 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 diff --git a/libs/utils/include/utils/sstream.h b/libs/utils/include/utils/sstream.h new file mode 100644 index 0000000000..d491966303 --- /dev/null +++ b/libs/utils/include/utils/sstream.h @@ -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 + +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 diff --git a/libs/utils/src/Log.cpp b/libs/utils/src/Log.cpp index 3c031259f5..e05575ddc4 100644 --- a/libs/utils/src/Log.cpp +++ b/libs/utils/src/Log.cpp @@ -26,129 +26,11 @@ # endif #endif -#if (!UTILS_TINY_IO) -# include -#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 diff --git a/libs/utils/src/ostream.cpp b/libs/utils/src/ostream.cpp new file mode 100644 index 0000000000..3b9424d922 --- /dev/null +++ b/libs/utils/src/ostream.cpp @@ -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 + +#include +#include + +#include + +#include + +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 diff --git a/libs/utils/src/sstream.cpp b/libs/utils/src/sstream.cpp new file mode 100644 index 0000000000..4bcc9a38d9 --- /dev/null +++ b/libs/utils/src/sstream.cpp @@ -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 + +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 diff --git a/libs/utils/test/test_sstream.cpp b/libs/utils/test/test_sstream.cpp new file mode 100644 index 0000000000..a0870c0d9f --- /dev/null +++ b/libs/utils/test/test_sstream.cpp @@ -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 + +#include + +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); +}