Squash development commits for PR

pull/5551/head
Steve M 2024-04-21 09:32:51 -07:00
parent b71b8f77ee
commit e3aa6d6a97
225 changed files with 146546 additions and 0 deletions

View File

@ -0,0 +1,120 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file USDLoader.cpp
* @brief Implementation of the USD importer class
*/
#ifndef ASSIMP_BUILD_NO_USD_IMPORTER
#include <memory>
// internal headers
#include <assimp/ai_assert.h>
#include <assimp/anim.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/fast_atof.h>
#include <assimp/Importer.hpp>
#include <assimp/importerdesc.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/IOSystem.hpp>
#include <assimp/scene.h>
#include <assimp/StringUtils.h>
#include <assimp/StreamReader.h>
#include "USDLoader.h"
#include "USDLoaderUtil.h"
static constexpr aiImporterDesc desc = {
"USD Object Importer",
"",
"",
"https://en.wikipedia.org/wiki/Universal_Scene_Description/",
aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
0,
0,
0,
0,
"usd usda usdc usdz"
};
namespace Assimp {
using namespace std;
// Constructor to be privately used by Importer
USDImporter::USDImporter() :
impl(USDImporterImplTinyusdz()) {
}
// ------------------------------------------------------------------------------------------------
bool USDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
// Based on token
static const uint32_t usdcTokens[] = { AI_MAKE_MAGIC("PXR-USDC") };
bool canRead = CheckMagicToken(pIOHandler, pFile, usdcTokens, AI_COUNT_OF(usdcTokens));
if (canRead) {
return canRead;
}
// Based on extension
if (isUsda(pFile) || isUsdc(pFile)) {
return true;
}
return true;
}
const aiImporterDesc *USDImporter::GetInfo() const {
return &desc;
}
void USDImporter::InternReadFile(
const std::string &pFile,
aiScene *pScene,
IOSystem *pIOHandler) {
impl.InternReadFile(
pFile,
pScene,
pIOHandler);
}
} // namespace Assimp
#endif // !! ASSIMP_BUILD_NO_USD_IMPORTER

View File

@ -0,0 +1,78 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file USDLoader.h
* @brief Declaration of the USD importer class.
*/
#pragma once
#ifndef AI_USDLOADER_H_INCLUDED
#define AI_USDLOADER_H_INCLUDED
#include <assimp/BaseImporter.h>
#include <assimp/types.h>
#include <vector>
#include <cstdint>
#include "USDLoaderImplTinyusdz.h"
namespace Assimp {
class USDImporter : public BaseImporter {
public:
USDImporter();
~USDImporter() override = default;
/// \brief Returns whether the class can handle the format of the given file.
/// \remark See BaseImporter::CanRead() for details.
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
protected:
//! \brief Appends the supported extension.
const aiImporterDesc *GetInfo() const override;
void InternReadFile(
const std::string &pFile,
aiScene *pScene,
IOSystem *pIOHandler) override;
private:
USDImporterImplTinyusdz impl;
};
} // namespace Assimp
#endif // AI_USDLOADER_H_INCLUDED

View File

@ -0,0 +1,643 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file USDLoader.cpp
* @brief Implementation of the USD importer class
*/
#ifndef ASSIMP_BUILD_NO_USD_IMPORTER
#include <memory>
#include <sstream>
// internal headers
#include <assimp/ai_assert.h>
#include <assimp/anim.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/fast_atof.h>
#include <assimp/Importer.hpp>
#include <assimp/importerdesc.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/IOSystem.hpp>
#include <assimp/scene.h>
#include <assimp/StringUtils.h>
#include <assimp/StreamReader.h>
#include "io-util.hh" // namespace tinyusdz::io
#include "tydra/scene-access.hh"
#include "tydra/shader-network.hh"
#include "USDLoaderImplTinyusdz.h"
#include "USDLoaderUtil.h"
#include "../../../contrib/tinyusdz/assimp_tinyusdz_logging.inc"
namespace {
const char *const TAG = "USDLoaderImplTinyusdz (C++)";
}
namespace Assimp {
using namespace std;
void USDImporterImplTinyusdz::InternReadFile(
const std::string &pFile,
aiScene *pScene,
IOSystem *pIOHandler) {
// Grab filename for logging purposes
size_t pos = pFile.find_last_of('/');
string basePath = pFile.substr(0, pos);
string nameWExt = pFile.substr(pos + 1);
(void) TAG; // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "InternReadFile(): model" << nameWExt;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
bool ret{ false };
tinyusdz::USDLoadOptions options;
tinyusdz::Stage stage;
std::string warn, err;
bool is_usdz{ false };
if (isUsdc(pFile)) {
ret = LoadUSDCFromFile(pFile, &stage, &warn, &err, options);
ss.str("");
ss << "InternReadFile(): LoadUSDCFromFile() result: " << ret;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
} else if (isUsda(pFile)) {
ret = LoadUSDAFromFile(pFile, &stage, &warn, &err, options);
ss.str("");
ss << "InternReadFile(): LoadUSDAFromFile() result: " << ret;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
} else if (isUsdz(pFile)) {
ret = LoadUSDZFromFile(pFile, &stage, &warn, &err, options);
is_usdz = true;
ss.str("");
ss << "InternReadFile(): LoadUSDZFromFile() result: " << ret;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
} else if (isUsd(pFile)) {
ret = LoadUSDFromFile(pFile, &stage, &warn, &err, options);
ss.str("");
ss << "InternReadFile(): LoadUSDFromFile() result: " << ret;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
}
if (warn.empty() && err.empty()) {
ss.str("");
ss << "InternReadFile(): load free of warnings/errors";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
} else {
if (!warn.empty()) {
ss.str("");
ss << "InternReadFile(): WARNING reported: " << warn;
TINYUSDZLOGW(TAG, "%s", ss.str().c_str());
}
if (!err.empty()) {
ss.str("");
ss << "InternReadFile(): ERROR reported: " << err;
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
}
}
if (!ret) {
ss.str("");
ss << "InternReadFile(): ERROR: load failed! ret: " << ret;
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
return;
}
tinyusdz::tydra::RenderScene render_scene;
tinyusdz::tydra::RenderSceneConverter converter;
tinyusdz::tydra::RenderSceneConverterEnv env(stage);
std::string usd_basedir = tinyusdz::io::GetBaseDir(pFile);
env.set_search_paths({ usd_basedir }); // {} needed to convert to vector of char
// NOTE: Pointer address of usdz_asset must be valid until the call of RenderSceneConverter::ConvertToRenderScene.
tinyusdz::USDZAsset usdz_asset;
if (is_usdz) {
if (!tinyusdz::ReadUSDZAssetInfoFromFile(pFile, &usdz_asset, &warn, &err)) {
if (!warn.empty()) {
ss.str("");
ss << "InternReadFile(): ReadUSDZAssetInfoFromFile: WARNING reported: " << warn;
TINYUSDZLOGW(TAG, "%s", ss.str().c_str());
}
if (!err.empty()) {
ss.str("");
ss << "InternReadFile(): ReadUSDZAssetInfoFromFile: ERROR reported: " << err;
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
}
ss.str("");
ss << "InternReadFile(): ReadUSDZAssetInfoFromFile: ERROR!";
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
} else {
ss.str("");
ss << "InternReadFile(): ReadUSDZAssetInfoFromFile: OK";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
}
tinyusdz::AssetResolutionResolver arr;
if (!tinyusdz::SetupUSDZAssetResolution(arr, &usdz_asset)) {
ss.str("");
ss << "InternReadFile(): SetupUSDZAssetResolution: ERROR: load failed! ret: " << ret;
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
} else {
ss.str("");
ss << "InternReadFile(): SetupUSDZAssetResolution: OK";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
env.asset_resolver = arr;
}
}
ret = converter.ConvertToRenderScene(env, &render_scene);
if (!ret) {
ss.str("");
ss << "InternReadFile(): ConvertToRenderScene() failed!";
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
return;
}
pScene->mNumMeshes = render_scene.meshes.size();
ss.str("");
ss << "InternReadFile(): mNumMeshes: " << pScene->mNumMeshes;
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]();
// Create root node
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
ss.str("");
ss << "InternReadFile(): mRootNode->mNumMeshes: " << pScene->mRootNode->mNumMeshes;
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
pScene->mRootNode->mMeshes = new unsigned int[pScene->mRootNode->mNumMeshes];
// Export meshes
for (size_t meshIdx = 0; meshIdx < pScene->mNumMeshes; meshIdx++) {
pScene->mMeshes[meshIdx] = new aiMesh();
pScene->mMeshes[meshIdx]->mName.Set(render_scene.meshes[meshIdx].prim_name);
if (render_scene.meshes[meshIdx].material_id > -1) {
pScene->mMeshes[meshIdx]->mMaterialIndex = render_scene.meshes[meshIdx].material_id;
}
verticesForMesh(render_scene, pScene, meshIdx, nameWExt);
facesForMesh(render_scene, pScene, meshIdx, nameWExt);
// Some models infer normals from faces, but others need them e.g.
// - apple "toy car" canopy normals will be wrong
// - human "untitled" model (tinyusdz issue #115) will be "splotchy"
normalsForMesh(render_scene, pScene, meshIdx, nameWExt);
materialsForMesh(render_scene, pScene, meshIdx, nameWExt);
uvsForMesh(render_scene, pScene, meshIdx, nameWExt);
pScene->mRootNode->mMeshes[meshIdx] = static_cast<unsigned int>(meshIdx);
}
nodes(render_scene, pScene, nameWExt);
materials(render_scene, pScene, nameWExt);
textures(render_scene, pScene, nameWExt);
textureImages(render_scene, pScene, nameWExt);
buffers(render_scene, pScene, nameWExt);
animations(render_scene, pScene, nameWExt);
}
void USDImporterImplTinyusdz::verticesForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt) {
pScene->mMeshes[meshIdx]->mNumVertices = render_scene.meshes[meshIdx].points.size();
pScene->mMeshes[meshIdx]->mVertices = new aiVector3D[pScene->mMeshes[meshIdx]->mNumVertices];
for (size_t j = 0; j < pScene->mMeshes[meshIdx]->mNumVertices; ++j) {
pScene->mMeshes[meshIdx]->mVertices[j].x = render_scene.meshes[meshIdx].points[j][0];
pScene->mMeshes[meshIdx]->mVertices[j].y = render_scene.meshes[meshIdx].points[j][1];
pScene->mMeshes[meshIdx]->mVertices[j].z = render_scene.meshes[meshIdx].points[j][2];
}
}
void USDImporterImplTinyusdz::facesForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt) {
pScene->mMeshes[meshIdx]->mNumFaces = render_scene.meshes[meshIdx].faceVertexCounts().size();
pScene->mMeshes[meshIdx]->mFaces = new aiFace[pScene->mMeshes[meshIdx]->mNumFaces]();
size_t faceVertIdxOffset = 0;
// stringstream ss;
// ss.str("");
// ss << "facesForMesh() for model " << nameWExt << " mesh[" << meshIdx << "]; " <<
// render_scene.meshes[meshIdx].faceVertexIndices().size() << " indices, " <<
// render_scene.meshes[meshIdx].faceVertexCounts().size() << " counts, type: " <<
// static_cast<int>(render_scene.meshes[meshIdx].vertexArrayType) <<
// ", Indexed: " << static_cast<int>(tinyusdz::tydra::RenderMesh::VertexArrayType::Indexed) <<
// ", Facevarying: " << static_cast<int>(tinyusdz::tydra::RenderMesh::VertexArrayType::Facevarying);
// TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
if (render_scene.meshes[meshIdx].vertexArrayType == tinyusdz::tydra::RenderMesh::VertexArrayType::Indexed) {
// ss.str("");
// ss << "vertexArrayType: Indexed";
// TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
} else {
// ss.str("");
// ss << "vertexArrayType: Facevarying";
// TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
}
for (size_t faceIdx = 0; faceIdx < pScene->mMeshes[meshIdx]->mNumFaces; ++faceIdx) {
pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices = render_scene.meshes[meshIdx].faceVertexCounts()[faceIdx];
pScene->mMeshes[meshIdx]->mFaces[faceIdx].mIndices = new unsigned int[pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices];
// ss.str("");
// ss << " m[" << meshIdx << "] f[" << faceIdx << "] o[" <<
// faceVertIdxOffset << "] N: " << pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices << ": ";
for (size_t j = 0; j < pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices; ++j) {
// ss << "i[" << j << "]: " <<
// render_scene.meshes[meshIdx].faceVertexIndices()[j + faceVertIdxOffset];
// if (j < pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices - 1) {
// ss << ", ";
// }
pScene->mMeshes[meshIdx]->mFaces[faceIdx].mIndices[j] =
render_scene.meshes[meshIdx].faceVertexIndices()[j + faceVertIdxOffset];
}
// TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
faceVertIdxOffset += pScene->mMeshes[meshIdx]->mFaces[faceIdx].mNumIndices;
}
}
void USDImporterImplTinyusdz::normalsForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt) {
stringstream ss;
pScene->mMeshes[meshIdx]->mNormals = new aiVector3D[pScene->mMeshes[meshIdx]->mNumVertices];
const float *floatPtr = reinterpret_cast<const float *>(render_scene.meshes[meshIdx].normals.get_data().data());
ss.str("");
ss << "normalsForMesh() for model " << nameWExt << " mesh[" << meshIdx << "]: " <<
"data size: " << render_scene.meshes[meshIdx].normals.get_data().size() <<
", num verts: " << pScene->mMeshes[meshIdx]->mNumVertices;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
for (size_t vertIdx = 0, fpj = 0; vertIdx < pScene->mMeshes[meshIdx]->mNumVertices; ++vertIdx, fpj += 3) {
pScene->mMeshes[meshIdx]->mNormals[vertIdx].x = floatPtr[fpj];
pScene->mMeshes[meshIdx]->mNormals[vertIdx].y = floatPtr[fpj + 1];
pScene->mMeshes[meshIdx]->mNormals[vertIdx].z = floatPtr[fpj + 2];
}
}
void USDImporterImplTinyusdz::materialsForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt) {
}
void USDImporterImplTinyusdz::uvsForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt) {
stringstream ss;
const size_t uvSlotsCount = render_scene.meshes[meshIdx].texcoords.size();
ss.str("");
ss << "uvsForMesh(): uvSlotsCount for mesh[" << meshIdx << "]: " << uvSlotsCount << " w/" <<
pScene->mMeshes[meshIdx]->mNumVertices << " vertices";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
if (uvSlotsCount < 1) {
return;
}
pScene->mMeshes[meshIdx]->mTextureCoords[0] = new aiVector3D[pScene->mMeshes[meshIdx]->mNumVertices];
pScene->mMeshes[meshIdx]->mNumUVComponents[0] = 2; // U and V stored in "x", "y" of aiVector3D.
for (size_t uvSlotIdx = 0; uvSlotIdx < uvSlotsCount; ++uvSlotIdx) {
const auto uvsForSlot = render_scene.meshes[meshIdx].texcoords.at(uvSlotIdx);
if (uvsForSlot.get_data().size() == 0) {
ss.str("");
ss << " NOTICE: uvSlotIdx: " << uvSlotIdx << " has " << uvsForSlot.get_data().size() << " bytes";
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
continue;
} else {
ss.str("");
ss << " uvSlotIdx: " << uvSlotIdx << " has " << uvsForSlot.get_data().size() << " bytes";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
}
const float *floatPtr = reinterpret_cast<const float *>(uvsForSlot.get_data().data());
for (size_t vertIdx = 0, fpj = 0; vertIdx < pScene->mMeshes[meshIdx]->mNumVertices; ++vertIdx, fpj += 2) {
pScene->mMeshes[meshIdx]->mTextureCoords[uvSlotIdx][vertIdx].x = floatPtr[fpj];
pScene->mMeshes[meshIdx]->mTextureCoords[uvSlotIdx][vertIdx].y = floatPtr[fpj + 1];
}
}
}
void USDImporterImplTinyusdz::nodes(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
const size_t numNodes{render_scene.nodes.size()};
(void) numNodes; // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "nodes(): model" << nameWExt << ", numNodes: " << numNodes;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
}
static aiColor3D *ownedColorPtrFor(const std::array<float, 3> &color) {
aiColor3D *colorPtr = new aiColor3D();
colorPtr->r = color[0];
colorPtr->g = color[1];
colorPtr->b = color[2];
return colorPtr;
}
static std::string nameForTextureWithId(
const tinyusdz::tydra::RenderScene &render_scene,
const int targetId) {
stringstream ss;
std::string texName;
for (const auto &image : render_scene.images) {
if (image.buffer_id == targetId) {
texName = image.asset_identifier;
ss.str("");
ss << "nameForTextureWithId(): found texture " << texName << " with target id " << targetId;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
break;
}
}
ss.str("");
ss << "nameForTextureWithId(): ERROR! Failed to find texture with target id " << targetId;
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
return texName;
}
static void assignTexture(
const tinyusdz::tydra::RenderScene &render_scene,
const tinyusdz::tydra::RenderMaterial &material,
aiMaterial *mat,
const int textureId,
const int aiTextureType) {
std::string name = nameForTextureWithId(render_scene, textureId);
aiString *texName = new aiString();
texName->Set(name);
stringstream ss;
ss.str("");
ss << "assignTexture(): name: " << name;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
// TODO: verify hard-coded '0' index is correct
mat->AddProperty(texName, _AI_MATKEY_TEXTURE_BASE, aiTextureType, 0);
}
void USDImporterImplTinyusdz::materials(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
const size_t numMaterials{render_scene.materials.size()};
(void) numMaterials; // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "materials(): model" << nameWExt << ", numMaterials: " << numMaterials;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
pScene->mNumMaterials = 0;
if (render_scene.materials.empty()) {
return;
}
pScene->mMaterials = new aiMaterial *[render_scene.materials.size()];
for (const auto &material : render_scene.materials) {
ss.str("");
ss << " material[" << pScene->mNumMaterials << "]: name: |" << material.name << "|, disp name: |" << material.display_name << "|";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
aiMaterial *mat = new aiMaterial;
aiString *materialName = new aiString();
materialName->Set(material.name);
mat->AddProperty(materialName, AI_MATKEY_NAME);
mat->AddProperty(
ownedColorPtrFor(material.surfaceShader.diffuseColor.value),
1, AI_MATKEY_COLOR_DIFFUSE);
mat->AddProperty(
ownedColorPtrFor(material.surfaceShader.specularColor.value),
1, AI_MATKEY_COLOR_SPECULAR);
mat->AddProperty(
ownedColorPtrFor(material.surfaceShader.emissiveColor.value),
1, AI_MATKEY_COLOR_EMISSIVE);
ss.str("");
if (material.surfaceShader.diffuseColor.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.diffuseColor.textureId, aiTextureType_DIFFUSE);
ss << " material[" << pScene->mNumMaterials << "]: diff tex id " << material.surfaceShader.diffuseColor.textureId << "\n";
}
if (material.surfaceShader.specularColor.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.specularColor.textureId, aiTextureType_SPECULAR);
ss << " material[" << pScene->mNumMaterials << "]: spec tex id " << material.surfaceShader.specularColor.textureId << "\n";
}
if (material.surfaceShader.normal.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.normal.textureId, aiTextureType_NORMALS);
ss << " material[" << pScene->mNumMaterials << "]: normal tex id " << material.surfaceShader.normal.textureId << "\n";
}
if (material.surfaceShader.emissiveColor.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.emissiveColor.textureId, aiTextureType_EMISSIVE);
ss << " material[" << pScene->mNumMaterials << "]: emissive tex id " << material.surfaceShader.emissiveColor.textureId << "\n";
}
if (material.surfaceShader.occlusion.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.occlusion.textureId, aiTextureType_LIGHTMAP);
ss << " material[" << pScene->mNumMaterials << "]: lightmap (occlusion) tex id " << material.surfaceShader.occlusion.textureId << "\n";
}
if (material.surfaceShader.metallic.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.metallic.textureId, aiTextureType_METALNESS);
ss << " material[" << pScene->mNumMaterials << "]: metallic tex id " << material.surfaceShader.metallic.textureId << "\n";
}
if (material.surfaceShader.roughness.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.roughness.textureId, aiTextureType_DIFFUSE_ROUGHNESS);
ss << " material[" << pScene->mNumMaterials << "]: roughness tex id " << material.surfaceShader.roughness.textureId << "\n";
}
if (material.surfaceShader.clearcoat.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.clearcoat.textureId, aiTextureType_CLEARCOAT);
ss << " material[" << pScene->mNumMaterials << "]: clearcoat tex id " << material.surfaceShader.clearcoat.textureId << "\n";
}
if (material.surfaceShader.opacity.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.opacity.textureId, aiTextureType_OPACITY);
ss << " material[" << pScene->mNumMaterials << "]: opacity tex id " << material.surfaceShader.opacity.textureId << "\n";
}
if (material.surfaceShader.displacement.is_texture()) {
assignTexture(render_scene, material, mat, material.surfaceShader.displacement.textureId, aiTextureType_DISPLACEMENT);
ss << " material[" << pScene->mNumMaterials << "]: displacement tex id " << material.surfaceShader.displacement.textureId << "\n";
}
if (material.surfaceShader.clearcoatRoughness.is_texture()) {
ss << " material[" << pScene->mNumMaterials << "]: clearcoatRoughness tex id " << material.surfaceShader.clearcoatRoughness.textureId << "\n";
}
if (material.surfaceShader.opacityThreshold.is_texture()) {
ss << " material[" << pScene->mNumMaterials << "]: opacityThreshold tex id " << material.surfaceShader.opacityThreshold.textureId << "\n";
}
if (material.surfaceShader.ior.is_texture()) {
ss << " material[" << pScene->mNumMaterials << "]: ior tex id " << material.surfaceShader.ior.textureId << "\n";
}
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
pScene->mMaterials[pScene->mNumMaterials] = mat;
++pScene->mNumMaterials;
}
}
void USDImporterImplTinyusdz::textures(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
const size_t numTextures{render_scene.textures.size()};
(void) numTextures; // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "textures(): model" << nameWExt << ", numTextures: " << numTextures;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
size_t i{0};
(void) i;
for (const auto &texture : render_scene.textures) {
(void) texture;
ss.str("");
ss << " texture[" << i << "]: id: " << texture.texture_image_id << ", disp name: |" << texture.display_name << "|, varname_uv: " <<
texture.varname_uv << ", prim_name: |" << texture.prim_name << "|, abs_path: |" << texture.abs_path << "|";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
++i;
}
}
/**
* "owned" as in, used "new" to allocate and aiScene now responsible for "delete"
*
* @param render_scene renderScene object
* @param image textureImage object
* @param nameWExt filename w/ext (use to extract file type hint)
* @return aiTexture ptr
*/
static aiTexture *ownedEmbeddedTextureFor(
const tinyusdz::tydra::RenderScene &render_scene,
const tinyusdz::tydra::TextureImage &image,
const std::string &nameWExt) {
stringstream ss;
aiTexture *tex = new aiTexture();
size_t pos = image.asset_identifier.find_last_of('/');
string embTexName{image.asset_identifier.substr(pos + 1)};
tex->mFilename.Set(image.asset_identifier.c_str());
tex->mHeight = image.height;
// const size_t imageBytesCount{render_scene.buffers[image.buffer_id].data.size() / image.channels};
tex->mWidth = image.width;
if (tex->mHeight == 0) {
pos = embTexName.find_last_of('.');
strncpy(tex->achFormatHint, embTexName.substr(pos + 1).c_str(), 3);
const size_t imageBytesCount{render_scene.buffers[image.buffer_id].data.size()};
tex->pcData = (aiTexel *) new char[imageBytesCount];
memcpy(tex->pcData, &render_scene.buffers[image.buffer_id].data[0], imageBytesCount);
} else {
strncpy(tex->achFormatHint, "rgba8888", 8);
const size_t imageTexelsCount{tex->mWidth * tex->mHeight};
tex->pcData = (aiTexel *) new char[imageTexelsCount * image.channels];
const float *floatPtr = reinterpret_cast<const float *>(&render_scene.buffers[image.buffer_id].data[0]);
ss.str("");
ss << "ownedEmbeddedTextureFor(): manual fill...";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
for (int i = 0, fpi = 0; i < imageTexelsCount; ++i, fpi += 4) {
tex->pcData[i].b = static_cast<uint8_t>(floatPtr[fpi] * 255);
tex->pcData[i].g = static_cast<uint8_t>(floatPtr[fpi + 1] * 255);
tex->pcData[i].r = static_cast<uint8_t>(floatPtr[fpi + 2] * 255);
tex->pcData[i].a = static_cast<uint8_t>(floatPtr[fpi + 3] * 255);
}
ss.str("");
ss << "ownedEmbeddedTextureFor(): imageTexelsCount: " << imageTexelsCount << ", channels: " << image.channels;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
}
return tex;
}
void USDImporterImplTinyusdz::textureImages(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
stringstream ss;
const size_t numTextureImages{render_scene.images.size()};
(void) numTextureImages; // Ignore unused variable when -Werror enabled
ss.str("");
ss << "textureImages(): model" << nameWExt << ", numTextureImages: " << numTextureImages;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
pScene->mTextures = nullptr; // Need to iterate over images before knowing if valid textures available
pScene->mNumTextures = 0;
for (const auto &image : render_scene.images) {
ss.str("");
ss << " image[" << pScene->mNumTextures << "]: |" << image.asset_identifier << "| w: " << image.width << ", h: " << image.height <<
", channels: " << image.channels << ", miplevel: " << image.miplevel << ", buffer id: " << image.buffer_id << "\n" <<
" buffers.size(): " << render_scene.buffers.size() << ", data empty? " << render_scene.buffers[image.buffer_id].data.empty();
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
if (image.buffer_id > -1 &&
image.buffer_id < render_scene.buffers.size() &&
!render_scene.buffers[image.buffer_id].data.empty()) {
aiTexture *tex = ownedEmbeddedTextureFor(
render_scene,
image,
nameWExt);
if (pScene->mTextures == nullptr) {
ss.str("");
ss << " Init pScene->mTextures[" << render_scene.images.size() << "]";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
pScene->mTextures = new aiTexture *[render_scene.images.size()];
}
ss.str("");
ss << " pScene->mTextures[" << pScene->mNumTextures << "] name: |" << tex->mFilename.C_Str() <<
"|, w: " << tex->mWidth << ", h: " << tex->mHeight << ", hint: " << tex->achFormatHint;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
pScene->mTextures[pScene->mNumTextures++] = tex;
}
}
}
void USDImporterImplTinyusdz::buffers(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
const size_t numBuffers{render_scene.buffers.size()};
(void) numBuffers; // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "buffers(): model" << nameWExt << ", numBuffers: " << numBuffers;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
size_t i = 0;
for (const auto &buffer : render_scene.buffers) {
ss.str("");
ss << " buffer[" << i << "]: count: " << buffer.data.size() << ", type: " << to_string(buffer.componentType);
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
++i;
}
}
void USDImporterImplTinyusdz::animations(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
const size_t numAnimations{render_scene.animations.size()};
(void) numAnimations; // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "animations(): model" << nameWExt << ", numAnimations: " << numAnimations;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
}
} // namespace Assimp
#endif // !! ASSIMP_BUILD_NO_USD_IMPORTER

View File

@ -0,0 +1,127 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file USDLoader.h
* @brief Declaration of the USD importer class.
*/
#pragma once
#ifndef AI_USDLOADER_IMPL_TINYUSDZ_H_INCLUDED
#define AI_USDLOADER_IMPL_TINYUSDZ_H_INCLUDED
#include <assimp/BaseImporter.h>
#include <assimp/types.h>
#include <vector>
#include <cstdint>
#include "tinyusdz.hh"
#include "tydra/render-data.hh"
namespace Assimp {
class USDImporterImplTinyusdz {
public:
USDImporterImplTinyusdz() = default;
~USDImporterImplTinyusdz() = default;
void InternReadFile(
const std::string &pFile,
aiScene *pScene,
IOSystem *pIOHandler);
void verticesForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt);
void facesForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt);
void normalsForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt);
void materialsForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt);
void uvsForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt);
void nodes(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt);
void materials(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt);
void textures(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt);
void textureImages(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt);
void buffers(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt);
void animations(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt);
};
} // namespace Assimp
#endif // AI_USDLOADER_IMPL_TINYUSDZ_H_INCLUDED

View File

@ -0,0 +1,116 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file USDLoader.cpp
* @brief Implementation of the USD importer class
*/
#ifndef ASSIMP_BUILD_NO_USD_IMPORTER
#include <memory>
// internal headers
#include <assimp/ai_assert.h>
#include <assimp/anim.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/fast_atof.h>
#include <assimp/Importer.hpp>
#include <assimp/importerdesc.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/IOSystem.hpp>
#include <assimp/scene.h>
#include <assimp/StringUtils.h>
#include <assimp/StreamReader.h>
#include "USDLoaderUtil.h"
namespace Assimp {
using namespace std;
bool isUsda(const std::string &pFile) {
size_t pos = pFile.find_last_of('.');
if (pos == string::npos) {
return false;
}
string ext = pFile.substr(pos + 1);
if (ext.size() != 4) {
return false;
}
return (ext[0] == 'u' || ext[0] == 'U') && (ext[1] == 's' || ext[1] == 'S') && (ext[2] == 'd' || ext[2] == 'D') && (ext[3] == 'a' || ext[3] == 'A');
}
bool isUsdc(const std::string &pFile) {
size_t pos = pFile.find_last_of('.');
if (pos == string::npos) {
return false;
}
string ext = pFile.substr(pos + 1);
if (ext.size() != 4) {
return false;
}
return (ext[0] == 'u' || ext[0] == 'U') && (ext[1] == 's' || ext[1] == 'S') && (ext[2] == 'd' || ext[2] == 'D') && (ext[3] == 'c' || ext[3] == 'C');
}
bool isUsdz(const std::string &pFile) {
size_t pos = pFile.find_last_of('.');
if (pos == string::npos) {
return false;
}
string ext = pFile.substr(pos + 1);
if (ext.size() != 4) {
return false;
}
return (ext[0] == 'u' || ext[0] == 'U') && (ext[1] == 's' || ext[1] == 'S') && (ext[2] == 'd' || ext[2] == 'D') && (ext[3] == 'z' || ext[3] == 'Z');
}
bool isUsd(const std::string &pFile) {
size_t pos = pFile.find_last_of('.');
if (pos == string::npos) {
return false;
}
string ext = pFile.substr(pos + 1);
if (ext.size() != 3) {
return false;
}
return (ext[0] == 'u' || ext[0] == 'U') && (ext[1] == 's' || ext[1] == 'S') && (ext[2] == 'd' || ext[2] == 'D');
}
} // namespace Assimp
#endif // !! ASSIMP_BUILD_NO_USD_IMPORTER

View File

@ -0,0 +1,59 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file USDLoader.h
* @brief Declaration of the USD importer class.
*/
#pragma once
#ifndef AI_USDLOADER_UTIL_H_INCLUDED
#define AI_USDLOADER_UTIL_H_INCLUDED
#include <assimp/BaseImporter.h>
#include <assimp/types.h>
#include <vector>
#include <cstdint>
namespace Assimp {
bool isUsda(const std::string &pFile);
bool isUsdc(const std::string &pFile);
bool isUsdz(const std::string &pFile);
bool isUsd(const std::string &pFile);
} // namespace Assimp
#endif // AI_USDLOADER_UTIL_H_INCLUDED

View File

@ -812,6 +812,15 @@ ADD_ASSIMP_IMPORTER( 3D
AssetLib/Unreal/UnrealLoader.h
)
ADD_ASSIMP_IMPORTER( USD
AssetLib/USD/USDLoader.cpp
AssetLib/USD/USDLoader.h
AssetLib/USD/USDLoaderImplTinyusdz.cpp
AssetLib/USD/USDLoaderImplTinyusdz.h
AssetLib/USD/USDLoaderUtil.cpp
AssetLib/USD/USDLoaderUtil.h
)
ADD_ASSIMP_IMPORTER( X
AssetLib/X/XFileHelper.h
AssetLib/X/XFileImporter.cpp
@ -905,6 +914,85 @@ SET( Extra_SRCS
)
SOURCE_GROUP( Extra FILES ${Extra_SRCS})
# USD/USDA/USDC/USDZ support
# tinyusdz
IF (NOT ASSIMP_BUILD_NO_USD_IMPORTER)
set(Tinyusdz_SRC_RELPATH "../contrib/tinyusdz/tinyusdz_repo/src")
set(Tinyusdz_SRCS
${Tinyusdz_SRC_RELPATH}/ascii-parser.cc
${Tinyusdz_SRC_RELPATH}/ascii-parser-basetype.cc
${Tinyusdz_SRC_RELPATH}/ascii-parser-timesamples.cc
${Tinyusdz_SRC_RELPATH}/ascii-parser-timesamples-array.cc
${Tinyusdz_SRC_RELPATH}/asset-resolution.cc
${Tinyusdz_SRC_RELPATH}/composition.cc
${Tinyusdz_SRC_RELPATH}/crate-format.cc
${Tinyusdz_SRC_RELPATH}/crate-pprint.cc
${Tinyusdz_SRC_RELPATH}/crate-reader.cc
${Tinyusdz_SRC_RELPATH}/image-loader.cc
${Tinyusdz_SRC_RELPATH}/image-util.cc
${Tinyusdz_SRC_RELPATH}/image-writer.cc
${Tinyusdz_SRC_RELPATH}/io-util.cc
${Tinyusdz_SRC_RELPATH}/linear-algebra.cc
${Tinyusdz_SRC_RELPATH}/path-util.cc
${Tinyusdz_SRC_RELPATH}/pprinter.cc
${Tinyusdz_SRC_RELPATH}/prim-composition.cc
${Tinyusdz_SRC_RELPATH}/prim-reconstruct.cc
${Tinyusdz_SRC_RELPATH}/prim-types.cc
${Tinyusdz_SRC_RELPATH}/primvar.cc
${Tinyusdz_SRC_RELPATH}/stage.cc
${Tinyusdz_SRC_RELPATH}/str-util.cc
${Tinyusdz_SRC_RELPATH}/tiny-format.cc
${Tinyusdz_SRC_RELPATH}/tinyusdz.cc
${Tinyusdz_SRC_RELPATH}/tydra/attribute-eval.cc
${Tinyusdz_SRC_RELPATH}/tydra/attribute-eval-typed.cc
${Tinyusdz_SRC_RELPATH}/tydra/attribute-eval-typed-animatable.cc
${Tinyusdz_SRC_RELPATH}/tydra/attribute-eval-typed-animatable-fallback.cc
${Tinyusdz_SRC_RELPATH}/tydra/attribute-eval-typed-fallback.cc
${Tinyusdz_SRC_RELPATH}/tydra/facial.cc
${Tinyusdz_SRC_RELPATH}/tydra/prim-apply.cc
${Tinyusdz_SRC_RELPATH}/tydra/render-data.cc
${Tinyusdz_SRC_RELPATH}/tydra/scene-access.cc
${Tinyusdz_SRC_RELPATH}/tydra/shader-network.cc
${Tinyusdz_SRC_RELPATH}/usda-reader.cc
${Tinyusdz_SRC_RELPATH}/usda-writer.cc
${Tinyusdz_SRC_RELPATH}/usdc-reader.cc
${Tinyusdz_SRC_RELPATH}/usdc-writer.cc
${Tinyusdz_SRC_RELPATH}/usdGeom.cc
${Tinyusdz_SRC_RELPATH}/usdLux.cc
${Tinyusdz_SRC_RELPATH}/usdMtlx.cc
${Tinyusdz_SRC_RELPATH}/usdShade.cc
${Tinyusdz_SRC_RELPATH}/usdSkel.cc
${Tinyusdz_SRC_RELPATH}/value-pprint.cc
${Tinyusdz_SRC_RELPATH}/value-types.cc
${Tinyusdz_SRC_RELPATH}/xform.cc
)
set(Tinyusdz_DEP_SOURCES
${Tinyusdz_SRC_RELPATH}/external/fpng.cpp
#${Tinyusdz_SRC_RELPATH}/external/staticstruct.cc
#${Tinyusdz_SRC_RELPATH}/external/string_id/database.cpp
#${Tinyusdz_SRC_RELPATH}/external/string_id/error.cpp
#${Tinyusdz_SRC_RELPATH}/external/string_id/string_id.cpp
#${Tinyusdz_SRC_RELPATH}/external/tinyxml2/tinyxml2.cpp
${Tinyusdz_SRC_RELPATH}/integerCoding.cpp
${Tinyusdz_SRC_RELPATH}/lz4-compression.cc
${Tinyusdz_SRC_RELPATH}/lz4/lz4.c
)
set(tinyusdz_INCLUDE_DIRS "../contrib/tinyusdz/tinyusdz_repo/src")
INCLUDE_DIRECTORIES(${tinyusdz_INCLUDE_DIRS})
SOURCE_GROUP( Contrib\\Tinyusdz
FILES
${Tinyusdz_SRCS}
${Tinyusdz_DEP_SOURCES}
)
MESSAGE(STATUS "tinyusdz enabled")
ELSE()
set(Tinyusdz_SRCS "")
set(Tinyusdz_DEP_SOURCES "")
MESSAGE(STATUS "tinyusdz disabled")
ENDIF()
# pugixml
IF(ASSIMP_HUNTER_ENABLED)
hunter_add_package(pugixml)
@ -1155,6 +1243,8 @@ SET( assimp_src
${openddl_parser_SRCS}
${open3dgc_SRCS}
${ziplib_SRCS}
${Tinyusdz_SRCS}
${Tinyusdz_DEP_SOURCES}
${Pugixml_SRCS}
${stb_SRCS}
# Necessary to show the headers in the project when using the VC++ generator:

View File

@ -55,6 +55,9 @@ corresponding preprocessor flag to selectively disable formats.
// Importers
// (include_new_importers_here)
// ------------------------------------------------------------------------------------------------
#if !defined(ASSIMP_BUILD_NO_USD_IMPORTER)
#include "AssetLib/USD/USDLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_X_IMPORTER
#include "AssetLib/X/XFileImporter.h"
#endif
@ -230,6 +233,9 @@ void GetImporterInstanceList(std::vector<BaseImporter *> &out) {
// (register_new_importers_here)
// ----------------------------------------------------------------------------
out.reserve(64);
#if !defined(ASSIMP_BUILD_NO_USD_IMPORTER)
out.push_back(new USDImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_X_IMPORTER)
out.push_back(new XFileImporter());
#endif

View File

@ -0,0 +1,40 @@
# tinyusdz
## Notes
Couldn't leverage tinyusdz CMakeLists.txt. Fell back to compiling source files specified in
"android" example.
## Assimp update history
### Apr 2024
Updated to [tinyusdz](https://github.com/syoyo/tinyusdz) branch `rendermesh-refactor` at 18 Mar 2024 commit `f9792ce67c4ef08d779cdf91f49ad97acc426466 `
### Mar 2024
Cloned github project [tinyusdz](https://github.com/syoyo/tinyusdz) branch `dev` at 10 Mar 2024 commit `912d27e8b632d2352e7284feb86584832c6015d5`
Removed folders:
- [.clusterfuzzlite](tinyusdz_repo%2F.clusterfuzzlite)
- [.git](tinyusdz_repo%2F.git)
- [.github](tinyusdz_repo%2F.github)
- [android](tinyusdz_repo%2Fandroid)
- [benchmarks](tinyusdz_repo%2Fbenchmarks)
- [cmake](tinyusdz_repo%2Fcmake)
- [data](tinyusdz_repo%2Fdata)
- [doc](tinyusdz_repo%2Fdoc)
- [examples](tinyusdz_repo%2Fexamples)
- [models](tinyusdz_repo%2Fmodels)
- [python](tinyusdz_repo%2Fpython)
- [sandbox](tinyusdz_repo%2Fsandbox)
- [schema](tinyusdz_repo%2Fschema)
- [scripts](tinyusdz_repo%2Fscripts)
- [tests](tinyusdz_repo%2Ftests)
Removed folders in `src`:
- [attic](tinyusdz_repo%2Fsrc%2Fattic)
- [blender](tinyusdz_repo%2Fsrc%2Fblender)
- [osd](tinyusdz_repo%2Fsrc%2Fosd)
Removed unused `.cc` files in `src`, `external` etc
Removed all files at root level except
- [LICENSE](tinyusdz_repo%2FLICENSE)
- [README.md](tinyusdz_repo%2FREADME.md)

View File

@ -0,0 +1,54 @@
/**
* Usage
* Add line below all other #include statements:
* #include "../../../assimp_tinyusdz_logging.inc"
* to files:
* - contrib/tinyusdz/tinyusdz_repo/src/tydra/render-data.cc
* - contrib/tinyusdz/tinyusdz_repo/src/tydra/scene-access.cc
*/
#pragma once
#if defined(__ANDROID__)
#include <sstream>
#include <android/log.h>
#define TINYUSDZLOGT(tag, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, tag, __VA_ARGS__))
#define TINYUSDZLOG0(tag, ...) ((void)__android_log_print(ANDROID_LOG_DEFAULT, tag, __VA_ARGS__))
#define TINYUSDZLOGD(tag, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, tag, __VA_ARGS__))
#define TINYUSDZLOGI(tag, ...) ((void)__android_log_print(ANDROID_LOG_INFO, tag, __VA_ARGS__))
#define TINYUSDZLOGW(tag, ...) ((void)__android_log_print(ANDROID_LOG_WARN, tag, __VA_ARGS__))
#define TINYUSDZLOGE(tag, ...) ((void)__android_log_print(ANDROID_LOG_ERROR, tag, __VA_ARGS__))
#else
#define TINYUSDZLOGT(tag, ...)
#define TINYUSDZLOG0(tag, ...)
#define TINYUSDZLOGD(tag, ...)
#define TINYUSDZLOGI(tag, ...)
#define TINYUSDZLOGW(tag, ...)
#define TINYUSDZLOGE(tag, ...)
#endif // #if defined(__ANDROID__)
#if defined(TINYUSDZ_LOCAL_DEBUG_PRINT)
#if defined(__ANDROID__)
// Works well but _extremely_ verbose
#define DCOUT(x) \
do { \
std::stringstream ss; \
ss << __FILE__ << ":" << __func__ << ":" \
<< std::to_string(__LINE__) << " " << x << "\n"; \
TINYUSDZLOGE("tinyusdz", "%s", ss.str().c_str()); \
} while (false)
// Silent version
//#define DCOUT(x) \
// do { \
// std::stringstream ss; \
// ss << __FILE__ << ":" << __func__ << ":" \
// << std::to_string(__LINE__) << " " << x << "\n"; \
// } while (false)
#else
#define DCOUT(x) \
do { \
std::cout << __FILE__ << ":" << __func__ << ":" \
<< std::to_string(__LINE__) << " " << x << "\n"; \
} while (false)
#endif // #if defined(__ANDROID__)
#endif // #if defined(TINYUSDZ_LOCAL_DEBUG_PRINT)

View File

@ -0,0 +1,16 @@
Apache 2.0 License
Copyright (c) 2020-2023 Syoyo Fujita
Copyright (c) 2024-Present Light Transport Entertainment Inc.
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.

View File

@ -0,0 +1,529 @@
# Tiny USDZ/USDA/USDC library in C++14
`TinyUSDZ` is secure, portable and dependency-free(depends only on C++ STL. Other 3rd-party libraries included. Yes, you don't need pxrUSD/OpenUSD library!) USDZ/USDC/USDA library written in C++14.
## High priority
* Tydra: Handy data structure converter for rendering https://github.com/syoyo/tinyusdz/issues/31
* Working on the branch: https://github.com/syoyo/tinyusdz/tree/rendermesh-refactor
* [ ] USD to RenderScene(OpenGL/Vulkan) conversion https://github.com/syoyo/tinyusdz/issues/109
* [ ] GeomSubset/Material Binding API support for shading/texturing https://github.com/syoyo/tinyusdz/issues/103
* [ ] UTF8 Identifier support https://github.com/syoyo/tinyusdz/issues/47
## Mid-term todo
* Collection API
* [ ] https://github.com/syoyo/tinyusdz/issues/108
* Experimental composition support https://github.com/syoyo/tinyusdz/issues/25
* [x] subLayers
* [x] references
* [x] payload(no delayed load)
* [x] inherits
* [x] variantSet
* [ ] Validate composition is correctly operated.
* Better usdLux support https://github.com/syoyo/tinyusdz/issues/101
* [ ] Support parsing usd-wg USD aasets
* https://github.com/syoyo/tinyusdz/issues/135
* Support reading & compose some production USD scenes
* [ ] Moana island v2.1 https://github.com/syoyo/tinyusdz/issues/90
* [ ] ALAB USD production scene https://github.com/syoyo/tinyusdz/issues/91
* MaterialX https://github.com/syoyo/tinyusdz/issues/86
* USD + MateriralX + PBR rendering example using https://github.com/lighttransport/pbrlab
* Improve interoperability with Blender USD export/import https://github.com/syoyo/tinyusdz/issues/98
* Example viewer
* [examples/openglviewer](examples/openglviewer) OpenGL viewer
* [examples/sdlviewer](examples/sdlviewer) Software raytracing viewer
## "What if" Experimental feature
* Gaussian Splatting support? https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/
## Build status
| | Linux | Windows | macOS | iOS | Android |
|:-------:|:---------------------------------------- |:------------------------------------- |:---------:|:------:|:-------:|
| dev | [![Linux Build](https://github.com/syoyo/tinyusdz/actions/workflows/linux_ci.yml/badge.svg)](https://github.com/syoyo/tinyusdz/actions/workflows/linux_ci.yml) | [![Windows CI build](https://github.com/syoyo/tinyusdz/actions/workflows/windows_ci.yml/badge.svg)](https://github.com/syoyo/tinyusdz/actions/workflows/windows_ci.yml) </br> [![Windows ARM CI build](https://github.com/syoyo/tinyusdz/actions/workflows/windows_arm_ci.yml/badge.svg)](https://github.com/syoyo/tinyusdz/actions/workflows/windows_arm_ci.yml) | [![macOS Build](https://github.com/syoyo/tinyusdz/actions/workflows/macos_ci.yml/badge.svg)](https://github.com/syoyo/tinyusdz/actions/workflows/macos_ci.yml) | [![iOS Build](https://github.com/syoyo/tinyusdz/actions/workflows/ios_ci.yml/badge.svg)](https://github.com/syoyo/tinyusdz/actions/workflows/ios_ci.yml) | [![Android arm64v8a Build](https://github.com/syoyo/tinyusdz/actions/workflows/android_ci.yml/badge.svg)](https://github.com/syoyo/tinyusdz/actions/workflows/android_ci.yml) |
## Supported platforms
| | Linux | Windows | macOS | iOS | Android | WASM(WASI) | WASM(Emscripten) |
|:-------:|:---------------------------------------- |:------------------------------------- |:---------:|:------:|:-------:|:------------------------------:|:-----------:|
| dev | ✅ 64bit </br> ✅ 32bit </br> ✅ aarch64 | ✅ 64bit </br> ✅ 32bit </br> ✅ ARM64/ARM32 |✅ |✅ |✅ |✅ [sandbox/wasi](sandbox/wasi) | ✅ [sandbox/emscripten](sandbox/emscripten) |
### Python binding(testing. currently not working)
https://pypi.org/project/tinyusdz/
Python binding is very early alpha testing stage. Not working at the moment.
You can install Python prebuilt wheel using
```
$ python -m pip install tinyusdz
```
| | Linux | Windows | macOS 11(Big Sur) or later | macos 10 |
|:-------:|:---------------------------------------- |:------------------------------------- |:-----------------------------:|:---------:|
| 3.6(⚠️) | ✅ 64bit </br> ✅ 32bit </br> ✅ aarch64 | ✅ 64bit </br> ✅ 32bit </br> ✅ ARM64 |🚫 | ✅ Intel |
| 3.7 | ✅ 64bit </br> ✅ 32bit </br> ✅ aarch64 | ✅ 64bit </br> ✅ 32bit </br> ✅ ARM64 |✅ arm64 | 🚫 universal2 </br> ✅ Intel |
| 3.8 | ✅ 64bit </br> ✅ 32bit </br> ✅ aarch64 | ✅ 64bit </br> ✅ 32bit </br> ✅ ARM64 |✅ arm64 | ✅ universal2 </br> ✅ Intel |
| 3.9 | ✅ 64bit </br> ✅ 32bit </br> ✅ aarch64 | ✅ 64bit </br> ✅ 32bit </br> ✅ ARM64 |✅ arm64 | ✅ universal2 </br> ✅ Intel |
| 3.10 | ✅ 64bit </br> ✅ 32bit </br> ✅ aarch64 | ✅ 64bit </br> ✅ 32bit </br> ✅ ARM64 |✅ arm64 | ✅ universal2 </br> ✅ Intel |
| 3.11 | ✅ 64bit </br> ✅ 32bit </br> ✅ aarch64 | ✅ 64bit </br> ✅ 32bit </br> ✅ ARM64 |✅ arm64 | ✅ universal2 </br> ✅ Intel |
⚠️ Python 3.6 is EOL and not recommended to use it. 3.6 bwheels is provided as long as cibuildwheels provides the build for it.
NOTE: Windows ARM64 binary is provided using cross-compiling. Its not well tested.
## Status
TinyUSDZ is in v0.8.0 release candidate.
Core loading feature(both USDA and USDC) is now working and production-grade(And no seg fault for corrupted USDA/USDC/USDZ input).
v0.8.0 is Flattened scene only(i.e, USDA/USDC generated by using pxrUSD's `usdcat --flatten` or USDZ scene).
Composition features are work-in-progress(experimental Composition feature support in v0.8.0. Better composition feature in next major release v0.9.0(Q4/2023 expected) planned)
Remaining tasks for v0.8.0 release are writing examples, demos and utility functions(Tydra. Especially access to Material/Shader attributes).
* [x] USDZ/USDC(Crate) parser
* USDC Crate version v0.8.0(most commonly used version as of 2022 Nov) or higher is supported.
* [ ] USDZ/USDC(Crate) writer (Work-in-progress)
* [x] USDA parser(Hand-written from a scratch. No Bison/Flex dependency!)
* [x] USDA writer
* [x] Support basic Primitives(Xform, Mesh, BasisCurves, etc.), basic Lights and Shaders(UsdPreviewSurface, UsdUVTexture, UsdPrimvarReader)
* **Experimental** support of some Composition features https://github.com/syoyo/tinyusdz/issues/25
* [x] subLayers
* [x] references
* [x] payload
* [x] inherits
* [x] variants
* [ ] specializes
**Please see** [doc/status.md](doc/status.md) **for more details**
* [ ] Basic C API(`c-tinyusd`) for language bindings
* [ ] [examples/c_api_example](examples/c_api_example)
* [ ] Basic Python binding
* [ ] Write simple SDL viewer example(2023 Winter expected)
* [ ] Write iOS and Android example(2023 Winter expected)
* [ ] Write Vision OS example?(2024 expected)
* [ ] Vulkan or OptiX/HIP RT raytracing viewer example
* [ ] USD <-> glTF converter example
* There is an independent work of USD to glTF binary GLB converter: https://github.com/fynv/usd2glb
* [ ] Web demo with Three.js?
* [ ] Three.js started to support USDZ with Ascii format, but no USDC support yet: https://github.com/mrdoob/three.js/issues/14219
## Discussions
We've opened Github Discussions page! https://github.com/syoyo/tinyusdz/discussions
### Security and memory budget
TinyUSDZ has first priority of considering security and stability.
USDZ(USDC) is a binary format. To avoid out-of-bounds access, out-of-memory, and other security issues when loading malcious USDZ(e.g. USDZ file from unknown origin), TinyUSDZ has a memory budget feature to avoid out-of-memory issue.
To limit a memory usage when loading USDZ file, Please set a value `max_memory_limit_in_mb` in USDLoadOptions.
TinyUSDZ source codes(and some external third party codes) are also checked by Address Sanitizer, CodeQL and Fuzzer.
#### Fuzzer
See [tests/fuzzer](tests/fuzzer) .
For building fuzzer tests, you'll need Meson and Ninja.
#### Web platform(WASM) and sandboxed environment(WASI)
If you need to deal with arbitrary USD files from unknown origin(e.g. from internet, NFT storage. Whose may contain malcious data), it is recommended to use TinyUSDZ in sandboxed environment(RunC, FlatPak, WASI(WASM)). Run in WASI is recommended at the moment.
TinyUSDZ does not use C++ exceptions and can be built without threads. TinyUSDZ supports WASM and WASI build. So TinyUSDZ should runs well on various Web platform(WebAssembly. No SharedArrayBuffer, Atomics and WebAssembly SIMD(which is not yet available on iOS Safari) required) and sandboxed environment(WASI. Users who need to read various USD file which possibly could contain malcious data from Internet, IPFS or blockchain storage).
See [sandbox/wasi/](sandbox/wasi) for Building TinyUSDZ with WASI toolchain.
### Tydra
USD itself is a generic container of 3D scene data.
Tydra is an interface to Renderers/Viewers and other DCCs.
Tydra may be something like Tiny version of pxrUSD Hydra, but its API is completely different. See [src/tydra/README.md](src/tydra/README.md) for the background.
* Image color space
* sRGB
* Linear
* Rec.709
* [ ] Partial support of OCIO(OpenColor IO) through TinyColorIO https://github.com/syoyo/tinycolorio . Currently SPI3DLut only.
* More details are T.B.W.
## Notice
TinyUSDZ does not support Reality Composer file format(`.reality`) since it uses proprietary file format and we cannot understand it(so no conversion support from/to Reality also).
## Commercial support
TinyUSDZ focuses on loading/writing USDA/USDC/USDZ functionalities.
Example viewer is just for demo purpose.
`syoyo` does not provide commercial support as an individual.
If you need commercial support, eco-system development(e.g. plug-ins, DCC tools on top of TinyUSDZ) or production-grade USDZ model viewer(e.g. embed TinyUSDZ to your AR app, 3D NFT Android mobile viewer capable of displaying (encrypted) USDZ model), please contact Light Transport Entertainment, Inc. : https://goo.gl/forms/1p6uGcOKWGpXPHkA2
We have a plan to manage TinyUSDZ project under Light Transport Entertainment Inc.
(By relicensing to Apatch 2.0)
## Projects using TinyUSDZ
* usd2glb: USD to glTF 2.0 GLB converter https://github.com/fynv/usd2glb
* webgpu-cpp-usdz: WebGPU C++/Wasm USDZ Renderer(NOTE: It doesn't support much yet!) https://github.com/Twinklebear/webgpu-cpp-usdz
### Other related projects
* UsdzSharpie: C# Simple implementation of Usdz file format ( https://github.com/UkooLabs/UsdzSharpie )
* TinyGLTF: glTF 2.0 loader/saver ( https://github.com/syoyo/tinygltf )
* BlenderUSDZ: It contains their own Python USDC parser/serializer. https://github.com/robmcrosby/BlenderUSDZ
## Supported platforms
* [x] Linux 64bit or later
* [x] ARM AARCH64
* [x] x86-64
* [ ] RISC-V(Should work)
* [ ] SPARC, POWER(Big endian machine). May work(theoretically)
* [x] Android arm64v8a
* [x] iOS
* [x] macOS(Arm, x86-64)
* [x] Windows 10 64bit or later
* [x] Windows ARM
* [x] clang-cl + MSVC SDK cross compile
* [x] WebAssembly
* Emscripten
* See [examples/sdlviewer/](examples/sdlviewer) example.
* [x] WASI(through WASI toolchain)
* See [sandbox/wasi](sandbox/wasi)
## Requirements
* C++14 compiler
* [x] gcc 4.9 or later
* [x] Visual Studio 2019 or later(2017 may compiles)
* VS2019 16.10 or later you can use `CMakePresets.json` for easier building.
* [x] Can be compiled with standalone MSVC compilers(Build Tools for Visual Studio 2019)
* [x] clang 3.4 or later https://clang.llvm.org/cxx_status.html
* [x] llvm-mingw(clang) supported
* [x] MinGW gcc supported, but not recommended(You may got compilation failure depending on your build configuration: https://github.com/syoyo/tinyusdz/issues/33 , and linking takes too much time if you use default bfd linker.). If you want to compile TinyUSDZ in MinGW environment, llvm-mingw(clang) is recommended to use.
Compilation with C++17 is also supported.
Compile on C++20 and C++23 could be possible, but not well tested, since C++20/C++23 compiler is not yet mature(as of 2024/01))
## Build
### Integrate to your app
If you are using CMake, just include tinyusdz repo with `add_subdirectory` and set include path to `<tinyusdz>/src`
We recommend to use CMake 3.24 or later.
(Mininum requirement is 3.16)
```cmake
...
# TinyUSDZ examples, tests and tools builds are disabled by default when
# tinyusdz is being built as a library with `add_subdirectory`
add_subdirectory(/path/to/tinyusdz tinyusdz)
target_include_directories(YOUR_APP PRIVATE "/path/to/tinyusdz/src")
# Namespaced static library target `tinyusdz::tinyusdz_static` is provided.
# At the moment we recommend to use static build of TinyUSDZ.
# You can use alias target `tinyusdz_static` also for legacy cmake version.
target_link_libraries(YOUR_APP PRIVATE tinyusdz::tinyusdz_static)
# For TinyUSDZ DLL(shared) library target, you can use
# `tinyusdz` library target
```
Another way is simply copy `src` folder to your app, and add `*.cc` files to your app's build system.
All include paths are set relative from `src` folder, so you can just add include directory to `src` folder.
See `<tinyusdz>/CMakeLists.txt` and [examples/sdlviewer/CMakeLists.txt](examples/sdlviewer/CMakeLists.txt) for details.
TinyUSDZ does not generate any header files and source files before the build and after the build(before the installation stage), so you don't need to take care of any pre-processing and post-processing of source tree. For example, USD Ascii parser uses hand-written C++ code so no Bison/flex/PEG processing involved.
It may not be recommend to use tinyusdz as a git submodule, since the repo contains lots of codes required to build TinyUSDZ examples but these are not required for your app.
### Compiler defines
Please see `CMake build options` and `CMakeLists.txt`. In most case same identifier is defined from cmake build options: For example if you specify `-DTINYUSDZ_PRODUCTION_BUILD=1` for cmake argument, `TINYUSDZ_PRODUCTION_BUILD` is defined.
### CMake
Cmake build is provided.
#### Linux and macOS
```
$ mkdir build
$ cd build
$ cmake ..
$ make
```
Please take a look at `scripts/bootstrap-cmake-*.sh` for some build configuraions.
#### Visual Studio
Visual Studio 2019 and 2022 are supported.
`CMakePresets.json` is provided for easier build on Visual Studio 2019 and Visual Studio 2022, but has lot of limitations(and seems parallel build is not working well so build is slow).
If you want flexibility, ordinary cmake `.sln` generation approach by invoking `vcsetup.bat` recommended.
(Edit VS version in `vcsetup.bat` as you with before the run)
#### LLVM-MinGW build
MinGW native and cross-compiling example using llvm-mingw(clang) is provided.
See `scripts/bootstrap-cmake-mingw-win.sh` and `scripts/bootstrap-cmake-llvm-mingw-cross.sh` for details.
One of benefit to use llvm-mingw is address sanitizer support on Windows app.
To run app(`.exe`, you'll need `libunwind.dll` and `libc++.dll` on your working directory or search path)
For Windows native build, we assume `ninja.exe` is installed on your system(You can use it from Meson package)
#### CMake build options
* `TINYUSDZ_PRODUCTION_BUILD` : Production build. Do not output debugging logs.
* `TINYUSDZ_BUILD_TESTS` : Build tests
* `TINYUSDZ_BUILD_EXAMPLES` : Build examples(note that not all examples in `examples` folder are built)
* `TINYUSDZ_WITH_OPENSUBDIV` : Use OpenSubviv to tessellate subdivision surface.
* OpenSubdiv code is included in TinyUSDZ repo. If you want to use external OpenSubdiv repo, specity the path to OpenSubdiv using `osd_DIR` cmake environment variable.
* `TINYUSDZ_WITH_AUDIO` : Support loading audio(mp3 and wav).
* `TINYUSDZ_WITH_EXR` : Support loading EXR format HDR texture through TinyEXR.
* `TINYUSDZ_WITH_PXR_COMPAT_API` : Build with pxrUSD compatible API.
#### clang-cl on Windows
Assume ninja.exe is installed and path to ninja.exe is added to your `%PATH%`
Edit path to MSVC SDK and Windows SDK in `bootstrap-clang-cl-win64.bat`, then
```
> bootstrap-clang-cl-win64.bat
> ninja.exe
```
### Tools and Examples
* [tusdcat](examples/tusdcat/) Parse USDZ/USDA/USDC and print it as Ascii(similar to `usdcat` in pxrUSD).
* `tusdcat` also do USD composition(`flatten`) and contains TinyUSDZ Composition API usecase.
* Deprecated. Use `tusdcat` [usda_parser](examples/usda_parser/) Parse USDA and print it as Ascii.
* Deprecated. Use `tusdcat` [usdc_parser](examples/usdc_parser/) Parse USDC and print it as Ascii.
* [Simple SDL viewer](examples/sdlviewer/)
* Separated CMake build provided: See [Readme](examples/sdlviewer/README.md)
* [api_tutorial](examples/api_tutorial/) Tutorial of TinyUSDZ Core API to construct a USD scene data.
* [tydra_api](examples/tydra_api/) Tutorial of TinyUSDZ Tydra API to access/query/convert a USD scene data.
* [asset_resolution](examples/asset_resolution/) Tutorial of using AssetResolutionResolver API to load USD from customized I/O(e.g. from Memory, Web, DB, ...)
* [file_format](examples/file_format/) Tutorial of using custom FileFormat handler to load Prim data in custom fileformat.
See [examples](examples) directory for more examples, but may not actively maintained except for the above examples.
### USDZ Data format
See [prim_format.md](doc/prim_format.md) and [preview_surface.md](doc/preview_surface.md)
## Example
### Minimum example to load USDA/USDC/USDZ file.
```
// TinyUSDZ is not a header-only library, so no TINYUSDZ_IMPLEMENTATIONS
#include "tinyusdz.hh"
// Include pprinter.hh and value-pprint.hh if you want to print TinyUSDZ classes/structs/enums.
// `tinyusdz::to_string()` and `std::operator<<` for TinyUSDZ classes/enums are provided separately for faster compilation
#include <iostream>
#include "pprinter.hh"
#include "value-pprint.hh"
int main(int argc, char **argv) {
std::string filename = "input.usd";
if (argc > 1) {
filename = argv[1];
}
tinyusdz::Stage stage; // Stage in USD terminology is nearly meant for Scene in generic 3D graphics terminology.
std::string warn;
std::string err;
// Auto detect USDA/USDC/USDZ
bool ret = tinyusdz::LoadUSDFromFile(filename, &stage, &warn, &err);
if (warn.size()) {
std::cout << "WARN : " << warn << "\n";
}
if (!ret) {
if (!err.empty()) {
std::cerr << "ERR : " << warn << "\n";
}
return EXIT_FAILURE;
}
// Print Stage(Scene graph)
std::cout << tinyusdz::to_string(stage) << "\n";
// You can also use ExportToString() as done in pxrUSD
// std::cout << stage.ExportToString() << "\n";
// stage.metas() To get Scene metadatum,
for (const Prim &root_prim : stage.root_prims()) {
std::cout << root_prim.absolute_path() << "\n";
// You can traverse Prim(scene graph object) using Prim::children()
// See examples/api_tutorial and examples/tydra_api for details.
}
return EXIT_SUCCESS;
}
```
### With Core TinyUSDZ API
Please see [api_tutorial](examples/api_tutorial/)
### With Tydra API
Please see [tydra_api](examples/tydra_api/)
## TODO
### Higher priority
* [ ] Built-in usdObj(wavefront .obj mesh) support.
* via tinyobjloader.
* [x] Support Crate(binary) version 0.8.0(USD v20.11 default)
* [ ] usdSkel utilities
* [ ] Joint hierachy reconstruction and compute skinning matrix(usdSkel)
* [ ] Blend shapes
* [x] Basic Blendshapes support
* [ ] In-between blend shapes
* [ ] Read USD data with bounded memory size. This feature is especially useful for mobile platform(e.g. in terms of security, memory consumption, etc)
* [ ] USDC writer
* [ ] Support Nested USDZ
* [ ] UDIM texture support
* [ ] MaterialX support
* [ ] Parse XML file using tinyxml2
### Middle priority
* [ ] Composition arcs
* [ ] Code refactoring, code optimization
### Lower priority
* [ ] iOS example?
* [ ] Support AR related schema(Game-like feature added by Reality Composer?)
* [ ] Audio play support
* [ ] Play audio using SoLoud or miniaudio(or Oboe for Android)
* [ ] wav(dr_wav)
* [ ] mp3(dr_mp3)
* [ ] m4a(ALAC?)
* [ ] Viewer with Vulkan API.
* [ ] Replace OpenSubdiv with our own subdiv library.
* [ ] Write parser based on the schema definition.
* [ ] Support big endian architecture.
## Python binding and prebuit packages
Python binding and prebuilt packages(uploadded on PyPI) are provided.
See [python/README.md](python/README.md) and [doc/python_binding.md](doc/python_binding.md) for details.
## CI build
CI build script is a build script trying to build TinyUSDZ in self-contained manner as much as possible(including custom Python build)
### Linux/macOS
T.B.W.
### Windows
Build custom Python,
```
> ci-build-python-lib.bat
```
then build TinyUSDZ by linking with this local Python build.
```
> ci-build-vs2022.bat
```
#### Cross compile with clang-cl + MSVC SDK on linux and run it on WINE(No Windows required at all solution!)
clang-cl(MSVC cl.exe) + MSVC SDK cross compile is also supported.
Please take a look at [doc/wine_cl.md](doc/wine_cl.md)
You can build pure Windows build of TinyUSDZ on Linux CI machine.
## License
TinyUSDZ is primarily licensed under Apache 2.0 license.
Some helper code is licensed under MIT license.
### Third party licenses
* pxrUSD : Apache 2.0 license. https://github.com/PixarAnimationStudios/USD
* OpenSubdiv : Apache 2.0 license. https://github.com/PixarAnimationStudios/OpenSubdiv
* lz4 : BSD-2 license. http://www.lz4.org
* cnpy(uncompressed ZIP decode/encode code) : MIT license https://github.com/rogersce/cnpy
* tinyexr: BSD license.
* tinyobjloader: MIT license.
* tinygltf: MIT license.
* tinycolorio: MIT license. https://github.com/syoyo/tinycolorio
* stb_image, stb_image_resize, stb_image_write, stb_truetype: public domain.
* dr_libs: public domain. https://github.com/mackron/dr_libs
* miniaudio: public domain or MIT no attribution. https://github.com/dr-soft/miniaudio
* SDL2 : zlib license. https://www.libsdl.org/index.php
* optional-lite: BSL 1.0 license. https://github.com/martinmoene/optional-lite
* expected-lite: BSL 1.0 license. https://github.com/martinmoene/expected-lite
* mapbox/earcut.hpp: ISC license. https://github.com/mapbox/earcut.hpp
* par_shapes.h generate parametric surfaces and other simple shapes: MIT license https://github.com/prideout/par
* MaterialX: Apache 2.0 license. https://github.com/AcademySoftwareFoundation/MaterialX
* string_id: zlib license. https://github.com/foonathan/string_id
* cityhash: MIT license. https://github.com/google/cityhash
* fast_float: Apache 2.0/MIT dual license. https://github.com/fastfloat/fast_float
* jsteeman/atoi: Apache 2.0 license. https://github.com/jsteemann/atoi
* formatxx: unlicense. https://github.com/seanmiddleditch/formatxx
* ubench.h: Unlicense. https://github.com/sheredom/ubench.h
* thelink2012/any : BSL-1.0 license. https://github.com/thelink2012/any
* simple_match : BSL-1.0 license. https://github.com/jbandela/simple_match
* nanobind : BSD-3 license. https://github.com/wjakob/nanobind
* pybind11 : BSD-3 license. https://github.com/pybind/pybind11
* pystring : BSD-3 license. https://github.com/imageworks/pystring
* gulrak/filesytem : MIT license. https://github.com/gulrak/filesystem
* p-ranav/glob : MIT license. https://github.com/p-ranav/glob
* linalg.h : Unlicense. https://github.com/sgorsten/linalg
* mapbox/eternal: ISC License. https://github.com/mapbox/eternal
* bvh: MIT license. https://github.com/madmann91/bvh
* dtoa_milo.h: MIT License. https://github.com/miloyip/dtoa-benchmark
* jeaiii/itoa: MIT License. https://github.com/jeaiii/itoa
* alac: Apache 2.0 License. https://macosforge.github.io/alac/
* OpenFBX: MIT License. https://github.com/nem0/OpenFBX
* floaxie: Apache 2.0 License. https://github.com/aclex/floaxie
* boost math sin_pi/cos_pi: BSL 1.0 License. https://www.boost.org/
* Vulkan: MIT License. https://github.com/SaschaWillems/Vulkan
* Metal.cpp: Apache 2.0 License. https://github.com/bkaradzic/metal-cpp https://developer.apple.com/metal/cpp/
* sRGB transform: MIT license. https://www.nayuki.io/page/srgb-transform-library
* virtualGizmo3D: BSD-2 license. https://github.com/BrutPitt/virtualGizmo3D
* nanozlib: Apache 2.0 license. https://github.com/lighttransport/nanozlib
* lz4.py: MIT license. https://github.com/SE2Dev/PyCoD/blob/master/_lz4.py
* pugixml: MIT license. https://github.com/zeux/pugixml
* nanoflann: 2-clause BSD license. https://github.com/jlblancoc/nanoflann
* tinymeshutils: MIT license. https://github.com/syoyo/tinymeshutils

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,338 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2021 - 2023, Syoyo Fujita.
// Copyright 2023 - Present, Light Transport Entertainment Inc.
//
// To deal with too many sections in generated .obj error(happens in MinGW and MSVC)
// Split ParseTimeSamples to two .cc files.
//
// TODO
// - [x] Rewrite code with less C++ template code.
#include <cstdio>
#ifdef _MSC_VER
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
#include <algorithm>
#include <atomic>
//#include <cassert>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <iterator>
#include <map>
#include <set>
#include <sstream>
#include <stack>
#if defined(__wasi__)
#else
#include <mutex>
#include <thread>
#endif
#include <vector>
#include "ascii-parser.hh"
#include "str-util.hh"
#include "tiny-format.hh"
//
#if !defined(TINYUSDZ_DISABLE_MODULE_USDA_READER)
//
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
// external
//#include "external/fast_float/include/fast_float/fast_float.h"
//#include "external/jsteemann/atoi.h"
//#include "external/simple_match/include/simple_match/simple_match.hpp"
#include "nonstd/expected.hpp"
//
#ifdef __clang__
#pragma clang diagnostic pop
#endif
//
// Tentative
#ifdef __clang__
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif
#include "common-macros.inc"
#include "io-util.hh"
#include "pprinter.hh"
#include "prim-types.hh"
#include "str-util.hh"
#include "stream-reader.hh"
#include "tinyusdz.hh"
#include "value-pprint.hh"
#include "value-types.hh"
namespace tinyusdz {
namespace ascii {
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<bool> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<int32_t> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<uint32_t> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<int64_t> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<uint64_t> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::half> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::half2> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::half3> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::half4> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<float> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::float2> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::float3> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::float4> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<double> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::double2> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::double3> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::double4> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord2h> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord2f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord2d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord3h> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord3f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord3d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::point3h> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::point3f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::point3d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::normal3h> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::normal3f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::normal3d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::vector3h> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::vector3f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::vector3d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color3h> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color3f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color3d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color4h> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color4f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color4d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::matrix2f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::matrix3f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::matrix4f> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::matrix2d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::matrix3d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::matrix4d> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::quath> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::quatf> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::quatd> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::token> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::StringData> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<std::string> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<Reference> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<Path> *result);
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::AssetPath> *result);
//
// -- impl ParseTimeSampleData
//
bool AsciiParser::ParseTimeSampleValueOfArrayType(const uint32_t type_id, value::Value *result) {
if (!result) {
return false;
}
if (MaybeNone()) {
(*result) = value::ValueBlock();
return true;
}
value::Value val;
#define PARSE_TYPE(__tyid, __type) \
if (__tyid == value::TypeTraits<__type>::type_id()) { \
std::vector<__type> typed_val; \
if (!ParseBasicTypeArray(&typed_val)) { \
PUSH_ERROR_AND_RETURN("Failed to parse value with requested type `" + value::GetTypeName(__tyid) + "[]`"); \
} \
val = value::Value(typed_val); \
} else
// NOTE: `string` does not support multi-line string.
PARSE_TYPE(type_id, value::AssetPath)
PARSE_TYPE(type_id, value::token)
PARSE_TYPE(type_id, std::string)
PARSE_TYPE(type_id, float)
PARSE_TYPE(type_id, int32_t)
PARSE_TYPE(type_id, uint32_t)
PARSE_TYPE(type_id, int64_t)
PARSE_TYPE(type_id, uint64_t)
PARSE_TYPE(type_id, value::half)
PARSE_TYPE(type_id, value::half2)
PARSE_TYPE(type_id, value::half3)
PARSE_TYPE(type_id, value::half4)
PARSE_TYPE(type_id, float)
PARSE_TYPE(type_id, value::float2)
PARSE_TYPE(type_id, value::float3)
PARSE_TYPE(type_id, value::float4)
PARSE_TYPE(type_id, double)
PARSE_TYPE(type_id, value::double2)
PARSE_TYPE(type_id, value::double3)
PARSE_TYPE(type_id, value::double4)
PARSE_TYPE(type_id, value::quath)
PARSE_TYPE(type_id, value::quatf)
PARSE_TYPE(type_id, value::quatd)
PARSE_TYPE(type_id, value::color3f)
PARSE_TYPE(type_id, value::color4f)
PARSE_TYPE(type_id, value::color3d)
PARSE_TYPE(type_id, value::color4d)
PARSE_TYPE(type_id, value::vector3f)
PARSE_TYPE(type_id, value::normal3f)
PARSE_TYPE(type_id, value::point3f)
PARSE_TYPE(type_id, value::texcoord2f)
PARSE_TYPE(type_id, value::texcoord3f)
PARSE_TYPE(type_id, value::matrix2f)
PARSE_TYPE(type_id, value::matrix3f)
PARSE_TYPE(type_id, value::matrix4f)
PARSE_TYPE(type_id, value::matrix2d)
PARSE_TYPE(type_id, value::matrix3d)
PARSE_TYPE(type_id, value::matrix4d) {
PUSH_ERROR_AND_RETURN(" : TODO: timeSamples type " + value::GetTypeName(type_id));
}
#undef PARSE_TYPE
(*result) = val;
return true;
}
// `type_name` does not contain "[]"
bool AsciiParser::ParseTimeSampleValueOfArrayType(const std::string &type_name, value::Value *result) {
nonstd::optional<uint32_t> type_id = value::TryGetTypeId(type_name);
if (!type_id) {
PUSH_ERROR_AND_RETURN("Unsupported/invalid type name: " + type_name);
}
return ParseTimeSampleValueOfArrayType(type_id.value(), result);
}
bool AsciiParser::ParseTimeSamplesOfArray(const std::string &type_name,
value::TimeSamples *ts_out) {
value::TimeSamples ts;
if (!Expect('{')) {
return false;
}
if (!SkipWhitespaceAndNewline()) {
return false;
}
while (!Eof()) {
char c;
if (!Char1(&c)) {
return false;
}
if (c == '}') {
break;
}
Rewind(1);
double timeVal;
// -inf, inf and nan are handled.
if (!ReadBasicType(&timeVal)) {
PushError("Parse time value failed.");
return false;
}
if (!SkipWhitespace()) {
return false;
}
if (!Expect(':')) {
return false;
}
if (!SkipWhitespace()) {
return false;
}
value::Value value;
if (!ParseTimeSampleValueOfArrayType(type_name, &value)) { // could be None(ValueBlock)
return false;
}
// The last element may have separator ','
{
// Semicolon ';' is not allowed as a separator for timeSamples array
// values.
if (!SkipWhitespace()) {
return false;
}
char sep{};
if (!Char1(&sep)) {
return false;
}
DCOUT("sep = " << sep);
if (sep == '}') {
// End of item
ts.add_sample(timeVal, value);
break;
} else if (sep == ',') {
// ok
} else {
Rewind(1);
// Look ahead Newline + '}'
auto loc = CurrLoc();
if (SkipWhitespaceAndNewline()) {
char nc;
if (!Char1(&nc)) {
return false;
}
if (nc == '}') {
// End of item
ts.add_sample(timeVal, value);
break;
}
}
// Rewind and continue parsing.
SeekTo(loc);
}
}
if (!SkipWhitespaceAndNewline()) {
return false;
}
ts.add_sample(timeVal, value);
}
DCOUT("Parse TimeSamples success. # of items = " << ts.size());
if (ts_out) {
(*ts_out) = std::move(ts);
}
return true;
}
} // namespace ascii
} // namespace tinyusdz
#else // TINYUSDZ_DISABLE_MODULE_USDA_READER
#endif // TINYUSDZ_DISABLE_MODULE_USDA_READER

View File

@ -0,0 +1,285 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2021 - 2023, Syoyo Fujita.
// Copyright 2023 - Present, Light Transport Entertainment Inc.
//
// To deal with too many sections in generated .obj error(happens in MinGW and MSVC)
// Split ParseTimeSamples to two .cc files.
//
// TODO
// - [x] Rewrite code with less C++ template code.
#include <cstdio>
#ifdef _MSC_VER
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
#include <algorithm>
#include <atomic>
//#include <cassert>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <iterator>
#include <map>
#include <set>
#include <sstream>
#include <stack>
#if defined(__wasi__)
#else
#include <mutex>
#include <thread>
#endif
#include <vector>
//
#if !defined(TINYUSDZ_DISABLE_MODULE_USDA_READER)
//
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
// external
//#include "external/fast_float/include/fast_float/fast_float.h"
//#include "external/jsteemann/atoi.h"
//#include "external/simple_match/include/simple_match/simple_match.hpp"
#include "nonstd/expected.hpp"
//
#ifdef __clang__
#pragma clang diagnostic pop
#endif
//
// Tentative
#ifdef __clang__
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif
#include "ascii-parser.hh"
#include "str-util.hh"
#include "tiny-format.hh"
//
#include "io-util.hh"
#include "pprinter.hh"
#include "prim-types.hh"
#include "str-util.hh"
#include "stream-reader.hh"
#include "tinyusdz.hh"
#include "value-pprint.hh"
#include "value-types.hh"
//
#include "common-macros.inc"
namespace tinyusdz {
namespace ascii {
bool AsciiParser::ParseTimeSampleValue(const uint32_t type_id, value::Value *result) {
if (!result) {
return false;
}
if (MaybeNone()) {
(*result) = value::ValueBlock();
return true;
}
value::Value val;
#define PARSE_TYPE(__tyid, __type) \
if (__tyid == value::TypeTraits<__type>::type_id()) { \
__type typed_val; \
if (!ReadBasicType(&typed_val)) { \
PUSH_ERROR_AND_RETURN("Failed to parse value with requested type `" + value::GetTypeName(__tyid) + "`"); \
} \
val = value::Value(typed_val); \
} else
// NOTE: `string` does not support multi-line string.
PARSE_TYPE(type_id, value::AssetPath)
PARSE_TYPE(type_id, value::token)
PARSE_TYPE(type_id, std::string)
PARSE_TYPE(type_id, float)
PARSE_TYPE(type_id, int32_t)
PARSE_TYPE(type_id, value::int2)
PARSE_TYPE(type_id, value::int3)
PARSE_TYPE(type_id, value::int4)
PARSE_TYPE(type_id, uint32_t)
PARSE_TYPE(type_id, int64_t)
PARSE_TYPE(type_id, uint64_t)
PARSE_TYPE(type_id, value::half)
PARSE_TYPE(type_id, value::half2)
PARSE_TYPE(type_id, value::half3)
PARSE_TYPE(type_id, value::half4)
PARSE_TYPE(type_id, float)
PARSE_TYPE(type_id, value::float2)
PARSE_TYPE(type_id, value::float3)
PARSE_TYPE(type_id, value::float4)
PARSE_TYPE(type_id, double)
PARSE_TYPE(type_id, value::double2)
PARSE_TYPE(type_id, value::double3)
PARSE_TYPE(type_id, value::double4)
PARSE_TYPE(type_id, value::quath)
PARSE_TYPE(type_id, value::quatf)
PARSE_TYPE(type_id, value::quatd)
PARSE_TYPE(type_id, value::color3f)
PARSE_TYPE(type_id, value::color4f)
PARSE_TYPE(type_id, value::color3d)
PARSE_TYPE(type_id, value::color4d)
PARSE_TYPE(type_id, value::vector3f)
PARSE_TYPE(type_id, value::normal3f)
PARSE_TYPE(type_id, value::point3f)
PARSE_TYPE(type_id, value::texcoord2f)
PARSE_TYPE(type_id, value::texcoord3f)
PARSE_TYPE(type_id, value::matrix2f)
PARSE_TYPE(type_id, value::matrix3f)
PARSE_TYPE(type_id, value::matrix4f)
PARSE_TYPE(type_id, value::matrix2d)
PARSE_TYPE(type_id, value::matrix3d)
PARSE_TYPE(type_id, value::matrix4d) {
PUSH_ERROR_AND_RETURN(" : TODO: timeSamples type " + value::GetTypeName(type_id));
}
#undef PARSE_TYPE
(*result) = val;
return true;
}
bool AsciiParser::ParseTimeSampleValue(const std::string &type_name, value::Value *result) {
nonstd::optional<uint32_t> type_id = value::TryGetTypeId(type_name);
if (!type_id) {
PUSH_ERROR_AND_RETURN("Unsupported/invalid timeSamples type " + type_name);
}
return ParseTimeSampleValue(type_id.value(), result);
}
bool AsciiParser::ParseTimeSamples(const std::string &type_name,
value::TimeSamples *ts_out) {
value::TimeSamples ts;
if (!Expect('{')) {
return false;
}
if (!SkipWhitespaceAndNewline()) {
return false;
}
while (!Eof()) {
char c;
if (!Char1(&c)) {
return false;
}
if (c == '}') {
break;
}
Rewind(1);
double timeVal;
// -inf, inf and nan are handled.
if (!ReadBasicType(&timeVal)) {
PushError("Parse time value failed.");
return false;
}
if (!SkipWhitespace()) {
return false;
}
if (!Expect(':')) {
return false;
}
if (!SkipWhitespace()) {
return false;
}
value::Value value;
if (!ParseTimeSampleValue(type_name, &value)) { // could be None(ValueBlock)
return false;
}
// The last element may have separator ','
{
// Semicolon ';' is not allowed as a separator for timeSamples array
// values.
if (!SkipWhitespace()) {
return false;
}
char sep{};
if (!Char1(&sep)) {
return false;
}
DCOUT("sep = " << sep);
if (sep == '}') {
// End of item
ts.add_sample(timeVal, value);
break;
} else if (sep == ',') {
// ok
} else {
Rewind(1);
// Look ahead Newline + '}'
auto loc = CurrLoc();
if (SkipWhitespaceAndNewline()) {
char nc;
if (!Char1(&nc)) {
return false;
}
if (nc == '}') {
// End of item
ts.add_sample(timeVal, value);
break;
}
}
// Rewind and continue parsing.
SeekTo(loc);
}
}
if (!SkipWhitespaceAndNewline()) {
return false;
}
ts.add_sample(timeVal, value);
}
DCOUT("Parse TimeSamples success. # of items = " << ts.size());
if (ts_out) {
(*ts_out) = std::move(ts);
}
return true;
}
} // namespace ascii
} // namespace tinyusdz
#else // TINYUSDZ_DISABLE_MODULE_USDA_READER
#endif // TINYUSDZ_DISABLE_MODULE_USDA_READER

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,893 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2021 - 2022, Syoyo Fujita.
// Copyright 2023 - Present, Light Transport Entertainment Inc.
//
// USD ASCII parser
#pragma once
// #include <functional>
#include <stdio.h>
#include <stack>
// #include "external/better-enums/enum.h"
#include "composition.hh"
#include "prim-types.hh"
#include "stream-reader.hh"
#include "tinyusdz.hh"
//
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
// external
#include "nonstd/expected.hpp"
//
#ifdef __clang__
#pragma clang diagnostic pop
#endif
namespace tinyusdz {
namespace ascii {
// keywords
constexpr auto kUniform = "uniform";
constexpr auto kToken = "token";
// Frequently used attr/meta keywords
constexpr auto kKind = "kind";
constexpr auto kInterpolation = "interpolation";
struct Identifier : std::string {
// using std::string;
};
// FIXME: Not used? remove.
struct PathIdentifier : std::string {
// using std::string;
};
// Parser option.
// For strict configuration(e.g. read USDZ on Mobile), should disallow unknown
// items.
struct AsciiParserOption {
bool allow_unknown_prim{true};
bool allow_unknown_apiSchema{true};
bool strict_allowedToken_check{false};
};
///
/// Test if input file is USDA ascii format.
///
bool IsUSDA(const std::string &filename, size_t max_filesize = 0);
class AsciiParser {
public:
// TODO: refactor
struct PrimMetas {
// Frequently used prim metas
nonstd::optional<Kind> kind;
value::dict customData; // `customData`
std::vector<value::StringData>
strings; // String only unregistered metadata.
};
// TODO: Unifity class with StageMetas in prim-types.hh
struct StageMetas {
///
/// Predefined Stage metas
///
std::vector<value::AssetPath> subLayers; // 'subLayers'
value::token defaultPrim; // 'defaultPrim'
value::StringData doc; // 'doc' or 'documentation'
nonstd::optional<Axis> upAxis; // not specified = nullopt
nonstd::optional<double> metersPerUnit;
nonstd::optional<double> timeCodesPerSecond;
nonstd::optional<double> startTimeCode;
nonstd::optional<double> endTimeCode;
nonstd::optional<double> framesPerSecond;
nonstd::optional<bool> autoPlay;
nonstd::optional<value::token> playbackMode; // 'none' or 'loop'
std::map<std::string, MetaVariable> customLayerData; // `customLayerData`.
value::StringData comment; // String only comment string.
};
struct ParseState {
int64_t loc{-1}; // byte location in StreamReder
};
struct Cursor {
int row{0};
int col{0};
};
struct ErrorDiagnostic {
std::string err;
Cursor cursor;
};
void PushError(const std::string &msg) {
ErrorDiagnostic diag;
diag.cursor.row = _curr_cursor.row;
diag.cursor.col = _curr_cursor.col;
diag.err = msg;
err_stack.push(diag);
}
// This function is used to cancel recent parsing error.
void PopError() {
if (!err_stack.empty()) {
err_stack.pop();
}
}
void PushWarn(const std::string &msg) {
ErrorDiagnostic diag;
diag.cursor.row = _curr_cursor.row;
diag.cursor.col = _curr_cursor.col;
diag.err = msg;
warn_stack.push(diag);
}
// This function is used to cancel recent parsing warning.
void PopWarn() {
if (!warn_stack.empty()) {
warn_stack.pop();
}
}
bool IsStageMeta(const std::string &name);
bool IsRegisteredPrimMeta(const std::string &name);
class VariableDef {
public:
// Handler functor in post parsing stage.
// e.g. Check input string is a valid one: one of "common", "group",
// "assembly", "component" or "subcomponent" for "kind" metadata
using PostParseHandler =
std::function<nonstd::expected<bool, std::string>(const std::string &)>;
static nonstd::expected<bool, std::string> DefaultPostParseHandler(
const std::string &) {
return true;
}
std::string type; // e.g. token, color3f
std::string name;
bool allow_array_type{false}; // when true, we accept `type` and `type[]`
PostParseHandler post_parse_handler;
VariableDef() = default;
VariableDef(const std::string &t, const std::string &n, bool a = false,
PostParseHandler ph = DefaultPostParseHandler)
: type(t), name(n), allow_array_type(a), post_parse_handler(ph) {}
VariableDef(const VariableDef &rhs) = default;
VariableDef &operator=(const VariableDef &rhs) = default;
// VariableDef &operator=(const VariableDef &rhs) {
// type = rhs.type;
// name = rhs.name;
// parse_handler = rhs.parse_handler;
// return *this;
//}
};
using PrimMetaMap =
std::map<std::string, std::pair<ListEditQual, MetaVariable>>;
struct VariantContent {
PrimMetaMap metas;
std::vector<int64_t> primIndices; // primIdx of Reconstrcuted Prim.
std::map<std::string, Property> props;
std::vector<value::token> properties;
// for nested `variantSet`
std::map<std::string, std::map<std::string, VariantContent>> variantSets;
};
// TODO: Use std::vector instead of std::map?
using VariantSetList =
std::map<std::string, std::map<std::string, VariantContent>>;
AsciiParser();
AsciiParser(tinyusdz::StreamReader *sr);
AsciiParser(const AsciiParser &rhs) = delete;
AsciiParser(AsciiParser &&rhs) = delete;
~AsciiParser();
///
/// Assign index to primitive for index-based prim scene graph representation.
/// -1 = root
///
using PrimIdxAssignFunctin = std::function<int64_t(const int64_t parentIdx)>;
void RegisterPrimIdxAssignFunction(PrimIdxAssignFunctin fun) {
_prim_idx_assign_fun = fun;
}
///
/// Stage Meta construction callback function
///
using StageMetaProcessFunction = std::function<bool(const StageMetas &metas)>;
///
/// Register Stage metadatum processing callback function.
/// Called when after parsing Stage metadatum.
///
void RegisterStageMetaProcessFunction(StageMetaProcessFunction fun) {
_stage_meta_process_fun = fun;
}
///
/// Prim Meta construction callback function
///
// using PrimMetaProcessFunction = std::function<bool(const PrimMetas
// &metas)>;
///
/// Prim construction callback function
/// TODO: Refactor arguments
///
/// @param[in] full_path Absolute Prim Path(e.g. "/scope/gmesh0")
/// @param[in] spec Specifier(`def`, `over` or `class`)
/// @param[in] primTypeName typeName of this Prim(e.g. "Mesh", "SphereLight")
/// @param[in] primIdx primitive index
/// @param[in] parentPrimIdx parent Prim index. -1 for root
/// @param[in] properties Prim properties
/// @param[in] in_meta Input Prim metadataum
/// @param[in] in_variantSetList Input VariantSet contents.
/// @return true upon success or error message.
///
using PrimConstructFunction =
std::function<nonstd::expected<bool, std::string>(
const Path &full_path, const Specifier spec,
const std::string &primTypeName, const Path &prim_name,
const int64_t primIdx, const int64_t parentPrimIdx,
const std::map<std::string, Property> &properties,
const PrimMetaMap &in_meta, const VariantSetList &in_variantSetList)>;
///
/// Register Prim construction callback function.
/// Example: "Xform", ReconstrctXform
///
void RegisterPrimConstructFunction(const std::string &prim_type,
PrimConstructFunction fun) {
_prim_construct_fun_map[prim_type] = fun;
}
///
/// Callbacks called at closing `def` block.
///
using PostPrimConstructFunction =
std::function<nonstd::expected<bool, std::string>(
const Path &path, const int64_t primIdx,
const int64_t parentPrimIdx)>;
void RegisterPostPrimConstructFunction(const std::string &prim_type,
PostPrimConstructFunction fun) {
_post_prim_construct_fun_map[prim_type] = fun;
}
///
/// For composition(Treat Prim as generic container).
/// AsciiParser(i.e. USDAReader)
///
using PrimSpecFunction = std::function<nonstd::expected<bool, std::string>(
const Path &full_path, const Specifier spec,
const std::string &primTypeName, const Path &prim_name,
const int64_t primIdx, const int64_t parentPrimIdx,
const std::map<std::string, Property> &properties,
const PrimMetaMap &in_meta, const VariantSetList &in_variantSetLists)>;
void RegisterPrimSpecFunction(PrimSpecFunction fun) { _primspec_fun = fun; }
///
/// Base filesystem directory to search asset files.
///
void SetBaseDir(const std::string &base_dir);
///
/// Set ASCII data stream
///
void SetStream(tinyusdz::StreamReader *sr);
///
/// Check if header data is USDA
///
bool CheckHeader();
///
/// True: create PrimSpec instead of typed Prim.
/// Set true if you do USD composition.
///
void set_primspec_mode(bool onoff) { _primspec_mode = onoff; }
///
/// Parser entry point
///
/// @param[in] load_states Bit mask of LoadState
/// @param[in] parser_option Parse option(optional)
///
/// TODO: Move `load_states` to AsciiParserOption?
///
bool Parse(
const uint32_t load_states = static_cast<uint32_t>(LoadState::Toplevel),
const AsciiParserOption &parser_option = AsciiParserOption());
///
/// Parse TimeSample value with specified array type of
/// `type_id`(value::TypeId) (You can obrain type_id from string using
/// value::GetTypeId())
///
bool ParseTimeSampleValue(const uint32_t type_id, value::Value *result);
///
/// Parse TimeSample value with specified `type_name`(Appears in USDA. .e.g.
/// "float", "matrix2d")
///
bool ParseTimeSampleValue(const std::string &type_name, value::Value *result);
///
/// Parse TimeSample value with specified base type of
/// `type_id`(value::TypeId) (You can obrain type_id from string using
/// value::GetTypeId())
///
bool ParseTimeSampleValueOfArrayType(const uint32_t base_type_id,
value::Value *result);
///
/// Parse TimeSample value with specified array type of `type_name`("[]"
/// omiotted. .e.g. "float" for "float[]")
///
bool ParseTimeSampleValueOfArrayType(const std::string &type_name,
value::Value *result);
// TODO: ParseBasicType?
bool ParsePurpose(Purpose *result);
///
/// Return true but `value` is set to nullopt for `None`(Attribute Blocked)
///
// template <typename T>
// bool ReadBasicType(nonstd::optional<T> *value);
bool ReadBasicType(nonstd::optional<bool> *value);
bool ReadBasicType(nonstd::optional<value::half> *value);
bool ReadBasicType(nonstd::optional<value::half2> *value);
bool ReadBasicType(nonstd::optional<value::half3> *value);
bool ReadBasicType(nonstd::optional<value::half4> *value);
bool ReadBasicType(nonstd::optional<int32_t> *value);
bool ReadBasicType(nonstd::optional<value::int2> *value);
bool ReadBasicType(nonstd::optional<value::int3> *value);
bool ReadBasicType(nonstd::optional<value::int4> *value);
bool ReadBasicType(nonstd::optional<uint32_t> *value);
bool ReadBasicType(nonstd::optional<value::uint2> *value);
bool ReadBasicType(nonstd::optional<value::uint3> *value);
bool ReadBasicType(nonstd::optional<value::uint4> *value);
bool ReadBasicType(nonstd::optional<int64_t> *value);
bool ReadBasicType(nonstd::optional<uint64_t> *value);
bool ReadBasicType(nonstd::optional<float> *value);
bool ReadBasicType(nonstd::optional<value::float2> *value);
bool ReadBasicType(nonstd::optional<value::float3> *value);
bool ReadBasicType(nonstd::optional<value::float4> *value);
bool ReadBasicType(nonstd::optional<double> *value);
bool ReadBasicType(nonstd::optional<value::double2> *value);
bool ReadBasicType(nonstd::optional<value::double3> *value);
bool ReadBasicType(nonstd::optional<value::double4> *value);
bool ReadBasicType(nonstd::optional<value::quath> *value);
bool ReadBasicType(nonstd::optional<value::quatf> *value);
bool ReadBasicType(nonstd::optional<value::quatd> *value);
bool ReadBasicType(nonstd::optional<value::point3h> *value);
bool ReadBasicType(nonstd::optional<value::point3f> *value);
bool ReadBasicType(nonstd::optional<value::point3d> *value);
bool ReadBasicType(nonstd::optional<value::vector3h> *value);
bool ReadBasicType(nonstd::optional<value::vector3f> *value);
bool ReadBasicType(nonstd::optional<value::vector3d> *value);
bool ReadBasicType(nonstd::optional<value::normal3h> *value);
bool ReadBasicType(nonstd::optional<value::normal3f> *value);
bool ReadBasicType(nonstd::optional<value::normal3d> *value);
bool ReadBasicType(nonstd::optional<value::color3h> *value);
bool ReadBasicType(nonstd::optional<value::color3f> *value);
bool ReadBasicType(nonstd::optional<value::color3d> *value);
bool ReadBasicType(nonstd::optional<value::color4h> *value);
bool ReadBasicType(nonstd::optional<value::color4f> *value);
bool ReadBasicType(nonstd::optional<value::color4d> *value);
bool ReadBasicType(nonstd::optional<value::matrix2f> *value);
bool ReadBasicType(nonstd::optional<value::matrix3f> *value);
bool ReadBasicType(nonstd::optional<value::matrix4f> *value);
bool ReadBasicType(nonstd::optional<value::matrix2d> *value);
bool ReadBasicType(nonstd::optional<value::matrix3d> *value);
bool ReadBasicType(nonstd::optional<value::matrix4d> *value);
bool ReadBasicType(nonstd::optional<value::texcoord2h> *value);
bool ReadBasicType(nonstd::optional<value::texcoord2f> *value);
bool ReadBasicType(nonstd::optional<value::texcoord2d> *value);
bool ReadBasicType(nonstd::optional<value::texcoord3h> *value);
bool ReadBasicType(nonstd::optional<value::texcoord3f> *value);
bool ReadBasicType(nonstd::optional<value::texcoord3d> *value);
bool ReadBasicType(nonstd::optional<value::StringData> *value);
bool ReadBasicType(nonstd::optional<std::string> *value);
bool ReadBasicType(nonstd::optional<value::token> *value);
bool ReadBasicType(nonstd::optional<Path> *value);
bool ReadBasicType(nonstd::optional<value::AssetPath> *value);
bool ReadBasicType(nonstd::optional<Reference> *value);
bool ReadBasicType(nonstd::optional<Payload> *value);
bool ReadBasicType(nonstd::optional<Identifier> *value);
bool ReadBasicType(nonstd::optional<PathIdentifier> *value);
// template <typename T>
// bool ReadBasicType(T *value);
bool ReadBasicType(bool *value);
bool ReadBasicType(value::half *value);
bool ReadBasicType(value::half2 *value);
bool ReadBasicType(value::half3 *value);
bool ReadBasicType(value::half4 *value);
bool ReadBasicType(int32_t *value);
bool ReadBasicType(value::int2 *value);
bool ReadBasicType(value::int3 *value);
bool ReadBasicType(value::int4 *value);
bool ReadBasicType(uint32_t *value);
bool ReadBasicType(value::uint2 *value);
bool ReadBasicType(value::uint3 *value);
bool ReadBasicType(value::uint4 *value);
bool ReadBasicType(int64_t *value);
bool ReadBasicType(uint64_t *value);
bool ReadBasicType(float *value);
bool ReadBasicType(value::float2 *value);
bool ReadBasicType(value::float3 *value);
bool ReadBasicType(value::float4 *value);
bool ReadBasicType(double *value);
bool ReadBasicType(value::double2 *value);
bool ReadBasicType(value::double3 *value);
bool ReadBasicType(value::double4 *value);
bool ReadBasicType(value::quath *value);
bool ReadBasicType(value::quatf *value);
bool ReadBasicType(value::quatd *value);
bool ReadBasicType(value::point3h *value);
bool ReadBasicType(value::point3f *value);
bool ReadBasicType(value::point3d *value);
bool ReadBasicType(value::vector3h *value);
bool ReadBasicType(value::vector3f *value);
bool ReadBasicType(value::vector3d *value);
bool ReadBasicType(value::normal3h *value);
bool ReadBasicType(value::normal3f *value);
bool ReadBasicType(value::normal3d *value);
bool ReadBasicType(value::color3h *value);
bool ReadBasicType(value::color3f *value);
bool ReadBasicType(value::color3d *value);
bool ReadBasicType(value::color4h *value);
bool ReadBasicType(value::color4f *value);
bool ReadBasicType(value::color4d *value);
bool ReadBasicType(value::texcoord2h *value);
bool ReadBasicType(value::texcoord2f *value);
bool ReadBasicType(value::texcoord2d *value);
bool ReadBasicType(value::texcoord3h *value);
bool ReadBasicType(value::texcoord3f *value);
bool ReadBasicType(value::texcoord3d *value);
bool ReadBasicType(value::matrix2f *value);
bool ReadBasicType(value::matrix3f *value);
bool ReadBasicType(value::matrix4f *value);
bool ReadBasicType(value::matrix2d *value);
bool ReadBasicType(value::matrix3d *value);
bool ReadBasicType(value::matrix4d *value);
bool ReadBasicType(value::StringData *value);
bool ReadBasicType(std::string *value);
bool ReadBasicType(value::token *value);
bool ReadBasicType(Path *value);
bool ReadBasicType(value::AssetPath *value);
bool ReadBasicType(Reference *value);
bool ReadBasicType(Payload *value);
bool ReadBasicType(Identifier *value);
bool ReadBasicType(PathIdentifier *value);
template <typename T>
bool ReadBasicType(nonstd::optional<std::vector<T>> *value);
template <typename T>
bool ReadBasicType(std::vector<T> *value);
bool ParseMatrix(value::matrix2f *result);
bool ParseMatrix(value::matrix3f *result);
bool ParseMatrix(value::matrix4f *result);
bool ParseMatrix(value::matrix2d *result);
bool ParseMatrix(value::matrix3d *result);
bool ParseMatrix(value::matrix4d *result);
///
/// Parse '(', Sep1By(','), ')'
///
template <typename T, size_t N>
bool ParseBasicTypeTuple(std::array<T, N> *result);
///
/// Parse '(', Sep1By(','), ')'
/// Can have `None`
///
template <typename T, size_t N>
bool ParseBasicTypeTuple(nonstd::optional<std::array<T, N>> *result);
template <typename T, size_t N>
bool ParseTupleArray(std::vector<std::array<T, N>> *result);
///
/// Parse the array of tuple. some may be None(e.g. `float3`: [(0, 1, 2),
/// None, (2, 3, 4), ...] )
///
template <typename T, size_t N>
bool ParseTupleArray(std::vector<nonstd::optional<std::array<T, N>>> *result);
template <typename T>
bool SepBy1BasicType(const char sep, std::vector<T> *result);
///
/// Allow the appearance of `sep` in the last item of array.
/// (e.g. `[1, 2, 3,]`)
///
template <typename T>
bool SepBy1BasicType(const char sep, const char end_symbol,
std::vector<T> *result);
///
/// Parse '[', Sep1By(','), ']'
///
template <typename T>
bool ParseBasicTypeArray(std::vector<nonstd::optional<T>> *result);
///
/// Parse '[', Sep1By(','), ']'
///
template <typename T>
bool ParseBasicTypeArray(std::vector<T> *result);
///
/// Parses 1 or more occurences of value with basic type 'T', separated by
/// `sep`
///
template <typename T>
bool SepBy1BasicType(const char sep,
std::vector<nonstd::optional<T>> *result);
///
/// Parses 1 or more occurences of tuple values with type 'T', separated by
/// `sep`. Allows 'None'
///
template <typename T, size_t N>
bool SepBy1TupleType(const char sep,
std::vector<nonstd::optional<std::array<T, N>>> *result);
///
/// Parses N occurences of tuple values with type 'T', separated by
/// `sep`. Allows 'None'
///
template <typename T, size_t M, size_t N>
bool SepByNTupleType(
const char sep,
std::array<nonstd::optional<std::array<T, M>>, N> *result);
///
/// Parses 1 or more occurences of tuple values with type 'T', separated by
/// `sep`
///
template <typename T, size_t N>
bool SepBy1TupleType(const char sep, std::vector<std::array<T, N>> *result);
bool ParseDictElement(std::string *out_key, MetaVariable *out_var);
bool ParseDict(std::map<std::string, MetaVariable> *out_dict);
///
/// Parse TimeSample data(scalar type) and store it to type-erased data
/// structure value::TimeSamples.
///
/// @param[in] type_name Name of TimeSamples type(seen in .usda file. e.g.
/// "float" for `float var.timeSamples = ..`)
///
bool ParseTimeSamples(const std::string &type_name, value::TimeSamples *ts);
///
/// Parse TimeSample data(array type) and store it to type-erased data
/// structure value::TimeSamples.
///
/// @param[in] type_name Name of TimeSamples type(seen in .usda file. array
/// suffix `[]` is omitted. e.g. "float" for `float[] var.timeSamples = ..`)
///
bool ParseTimeSamplesOfArray(const std::string &type_name,
value::TimeSamples *ts);
///
/// `variants` in Prim meta.
///
bool ParseVariantsElement(std::string *out_key, std::string *out_var);
bool ParseVariants(VariantSelectionMap *out_map);
bool MaybeListEditQual(tinyusdz::ListEditQual *qual);
bool MaybeVariability(tinyusdz::Variability *variability,
bool *varying_authored);
///
/// Try parsing single-quoted(`"`) string
///
bool MaybeString(value::StringData *str);
///
/// Try parsing triple-quited(`"""`) multi-line string.
///
bool MaybeTripleQuotedString(value::StringData *str);
///
/// Parse assset path identifier.
///
bool ParseAssetIdentifier(value::AssetPath *out, bool *triple_deliminated);
#if 0
///
///
///
std::string GetDefaultPrimName() const;
///
/// Get parsed toplevel "def" nodes(GPrim)
///
std::vector<GPrim> GetGPrims();
#endif
class PrimIterator;
using const_iterator = PrimIterator;
const_iterator begin() const;
const_iterator end() const;
///
/// Get error message(when `Parse` failed)
///
std::string GetError();
///
/// Get warning message(warnings in `Parse`)
///
std::string GetWarning();
#if 0
// Return the flag if the .usda is read from `references`
bool IsReferenced() { return _referenced; }
// Return the flag if the .usda is read from `subLayers`
bool IsSubLayered() { return _sub_layered; }
// Return the flag if the .usda is read from `payload`
bool IsPayloaded() { return _payloaded; }
#endif
// Return true if the .udsa is read in the top layer(stage)
bool IsToplevel() {
return _toplevel;
// return !IsReferenced() && !IsSubLayered() && !IsPayloaded();
}
bool MaybeNone();
bool MaybeCustom();
template <typename T>
bool MaybeNonFinite(T *out);
bool LexFloat(std::string *result);
bool Expect(char expect_c);
bool ReadStringLiteral(
std::string *literal); // identifier wrapped with " or '. result contains
// quote chars.
bool ReadPrimAttrIdentifier(std::string *token);
bool ReadIdentifier(std::string *token); // no '"'
bool ReadPathIdentifier(
std::string *path_identifier); // '<' + identifier + '>'
// read until newline
bool ReadUntilNewline(std::string *str);
/// Parse magic
/// #usda FLOAT
bool ParseMagicHeader();
bool SkipWhitespace();
// skip_semicolon true: ';' can be used as a separator. this flag is for
// statement block.
bool SkipWhitespaceAndNewline(const bool allow_semicolon = true);
bool SkipCommentAndWhitespaceAndNewline(const bool allow_semicolon = true);
bool SkipUntilNewline();
// bool ParseAttributeMeta();
bool ParseAttrMeta(AttrMeta *out_meta);
bool ParsePrimMetas(PrimMetaMap *out_metamap);
bool ParseMetaValue(const VariableDef &def, MetaVariable *outvar);
bool ParseStageMetaOpt();
// Parsed Stage metadatum is stored in this instance.
bool ParseStageMetas();
bool ParseCustomMetaValue();
bool ParseReference(Reference *out, bool *triple_deliminated);
bool ParsePayload(Payload *out, bool *triple_deliminated);
// `#` style comment
bool ParseSharpComment();
bool IsSupportedPrimAttrType(const std::string &ty);
bool IsSupportedPrimType(const std::string &ty);
bool IsSupportedAPISchema(const std::string &ty);
bool Eof() {
// end of buffer, or current char is nullchar('\0')
return _sr->eof() || _sr->is_nullchar();
}
bool ParseRelationship(Relationship *result);
bool ParseProperties(std::map<std::string, Property> *props,
std::vector<value::token> *propNames);
//
// Look***() : Fetch chars but do not change input stream position.
//
bool LookChar1(char *c);
bool LookCharN(size_t n, std::vector<char> *nc);
bool Char1(char *c);
bool CharN(size_t n, std::vector<char> *nc);
bool Rewind(size_t offset);
uint64_t CurrLoc();
bool SeekTo(uint64_t pos); // Move to absolute `pos` bytes location
bool PushParserState();
bool PopParserState(ParseState *state);
//
// Valid after ParseStageMetas() --------------
//
StageMetas GetStageMetas() const { return _stage_metas; }
// primIdx is assigned through `PrimIdxAssignFunctin`
// parentPrimIdx = -1 => root prim
// depth = tree level(recursion count)
// bool ParseClassBlock(const int64_t primIdx, const int64_t parentPrimIdx,
// const uint32_t depth = 0); bool ParseOverBlock(const int64_t primIdx, const
// int64_t parentPrimIdx, const uint32_t depth = 0); bool ParseDefBlock(const
// int64_t primIdx, const int64_t parentPrimIdx, const uint32_t depth = 0);
// Parse `def`, `over` or `class` block
// @param[in] in_variantStmt : true when this Block is parsed within
// `variantSet` statement. Default true.
bool ParseBlock(const Specifier spec, const int64_t primIdx,
const int64_t parentPrimIdx, const uint32_t depth,
const bool in_variant = false);
// Parse `varianntSet` stmt
bool ParseVariantSet(const int64_t primIdx, const int64_t parentPrimIdx,
const uint32_t depth,
std::map<std::string, VariantContent> *variantSetMap);
// --------------------------------------------
private:
///
/// Do common setups. Assume called in ctor.
///
void Setup();
nonstd::optional<std::pair<ListEditQual, MetaVariable>> ParsePrimMeta();
bool ParsePrimProps(std::map<std::string, Property> *props,
std::vector<value::token> *propNames);
template <typename T>
bool ParseBasicPrimAttr(bool array_qual, const std::string &primattr_name,
Attribute *out_attr);
bool ParseStageMeta(std::pair<ListEditQual, MetaVariable> *out);
nonstd::optional<VariableDef> GetStageMetaDefinition(const std::string &name);
nonstd::optional<VariableDef> GetPrimMetaDefinition(const std::string &arg);
nonstd::optional<VariableDef> GetPropMetaDefinition(const std::string &arg);
std::string GetCurrentPrimPath();
bool PrimPathStackDepth() { return _path_stack.size(); }
void PushPrimPath(const std::string &abs_path) {
// TODO: validate `abs_path` is really absolute full path.
_path_stack.push(abs_path);
}
void PopPrimPath() {
if (!_path_stack.empty()) {
_path_stack.pop();
}
}
const tinyusdz::StreamReader *_sr = nullptr;
// "class" defs
// std::map<std::string, Klass> _klasses;
std::stack<std::string> _path_stack;
Cursor _curr_cursor;
// Supported Prim types
std::set<std::string> _supported_prim_types;
std::set<std::string> _supported_prim_attr_types;
// Supported API schemas
std::set<std::string> _supported_api_schemas;
// Supported metadataum for Stage
std::map<std::string, VariableDef> _supported_stage_metas;
// Supported metadataum for Prim.
std::map<std::string, VariableDef> _supported_prim_metas;
// Supported metadataum for Property(Attribute and Relation).
std::map<std::string, VariableDef> _supported_prop_metas;
std::stack<ErrorDiagnostic> err_stack;
std::stack<ErrorDiagnostic> warn_stack;
std::stack<ParseState> parse_stack;
float _version{1.0f};
// load flags
bool _toplevel{true};
// TODO: deprecate?
bool _sub_layered{false};
bool _referenced{false};
bool _payloaded{false};
AsciiParserOption _option;
std::string _base_dir;
StageMetas _stage_metas;
//
// Callbacks
//
PrimIdxAssignFunctin _prim_idx_assign_fun;
StageMetaProcessFunction _stage_meta_process_fun;
// PrimMetaProcessFunction _prim_meta_process_fun;
std::map<std::string, PrimConstructFunction> _prim_construct_fun_map;
std::map<std::string, PostPrimConstructFunction> _post_prim_construct_fun_map;
bool _primspec_mode{false};
// For composition. PrimSpec is typeless so single callback function only.
PrimSpecFunction _primspec_fun{nullptr};
};
///
/// For USDC.
/// Parse string representation of UnregisteredValue(Attribute value).
/// e.g. "[(0, 1), (2, 3)]" for uint2[] type
///
/// @param[in] typeName typeName(e.g. "uint2")
/// @param[in] str Ascii representation of value.
/// @param[out] value Ascii representation of value.
/// @param[out] err Parse error message when returning false.
///
bool ParseUnregistredValue(const std::string &typeName, const std::string &str,
value::Value *value, std::string *err);
} // namespace ascii
} // namespace tinyusdz

View File

@ -0,0 +1,202 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022 - Present, Light Transport Entertainment, Inc.
#include <cassert>
#include <iostream>
#include "asset-resolution.hh"
#include "common-macros.inc"
#include "io-util.hh"
#include "value-pprint.hh"
#include "str-util.hh"
namespace tinyusdz {
std::string AssetResolutionResolver::search_paths_str() const {
std::string str;
str += "[ ";
for (size_t i = 0; i < _search_paths.size(); i++) {
if (i > 0) {
str += ", ";
}
// TODO: Escape character?
str += _search_paths[i];
}
str += " ]";
return str;
}
bool AssetResolutionResolver::find(const std::string &assetPath) const {
DCOUT("search_paths = " << _search_paths);
DCOUT("assetPath = " << assetPath);
std::string ext = io::GetFileExtension(assetPath);
if (_asset_resolution_handlers.count(ext)) {
if (_asset_resolution_handlers.at(ext).resolve_fun && _asset_resolution_handlers.at(ext).size_fun) {
std::string resolvedPath;
std::string err;
// Use custom handler's userdata
void *userdata = _asset_resolution_handlers.at(ext).userdata;
int ret = _asset_resolution_handlers.at(ext).resolve_fun(assetPath.c_str(), _search_paths, &resolvedPath, &err, userdata);
if (ret != 0) {
return false;
}
uint64_t sz{0};
ret = _asset_resolution_handlers.at(ext).size_fun(resolvedPath.c_str(), &sz, &err, userdata);
if (ret != 0) {
return false;
}
return sz > 0;
} else {
DCOUT("Either Resolve function or Size function is nullptr. Fallback to built-in file handler.");
}
}
if ((_current_working_path == ".") || (_current_working_path == "./")) {
std::string rpath = io::FindFile(assetPath, {});
} else {
// TODO: Only find when input path is relative.
std::string rpath = io::FindFile(assetPath, {_current_working_path});
if (rpath.size()) {
return true;
}
}
// TODO: Cache resolition.
std::string fpath = io::FindFile(assetPath, _search_paths);
return fpath.size();
}
std::string AssetResolutionResolver::resolve(
const std::string &assetPath) const {
std::string ext = io::GetFileExtension(assetPath);
if (_asset_resolution_handlers.count(ext)) {
if (_asset_resolution_handlers.at(ext).resolve_fun) {
std::string resolvedPath;
std::string err;
// Use custom handler's userdata
void *userdata = _asset_resolution_handlers.at(ext).userdata;
int ret = _asset_resolution_handlers.at(ext).resolve_fun(assetPath.c_str(), _search_paths, &resolvedPath, &err, userdata);
if (ret != 0) {
return std::string();
}
return resolvedPath;
} else {
DCOUT("Resolve function is nullptr. Fallback to built-in file handler.");
}
}
DCOUT("cwd = " << _current_working_path);
DCOUT("search_paths = " << _search_paths);
DCOUT("assetPath = " << assetPath);
std::string rpath;
if ((_current_working_path == ".") || (_current_working_path == "./")) {
rpath = io::FindFile(assetPath, {});
} else {
rpath = io::FindFile(assetPath, {_current_working_path});
}
if (rpath.size()) {
return rpath;
}
// TODO: Cache resolition.
return io::FindFile(assetPath, _search_paths);
}
bool AssetResolutionResolver::open_asset(const std::string &resolvedPath, const std::string &assetPath,
Asset *asset_out, std::string *warn, std::string *err) const {
if (!asset_out) {
if (err) {
(*err) = "`asset` arg is nullptr.";
}
return false;
}
DCOUT("Opening asset: " << resolvedPath);
(void)assetPath;
(void)warn;
std::string ext = io::GetFileExtension(resolvedPath);
if (_asset_resolution_handlers.count(ext)) {
if (_asset_resolution_handlers.at(ext).size_fun && _asset_resolution_handlers.at(ext).read_fun) {
// Use custom handler's userdata
void *userdata = _asset_resolution_handlers.at(ext).userdata;
// Get asset size.
uint64_t sz{0};
int ret = _asset_resolution_handlers.at(ext).size_fun(resolvedPath.c_str(), &sz, err, userdata);
if (ret != 0) {
if (err) {
(*err) += "Get size of asset through handler failed.\n";
}
return false;
}
DCOUT("asset_size: " << sz);
tinyusdz::Asset asset;
asset.resize(size_t(sz));
uint64_t read_size{0};
ret = _asset_resolution_handlers.at(ext).read_fun(resolvedPath.c_str(), /* req_size */asset.size(), asset.data(), &read_size, err, userdata);
if (ret != 0) {
if (err) {
(*err) += "Read asset through handler failed.\n";
}
return false;
}
if (read_size < sz) {
asset.resize(size_t(read_size));
// May optimize memory usage
asset.shrink_to_fit();
}
(*asset_out) = std::move(asset);
return true;
} else {
DCOUT("Resolve function is nullptr. Fallback to built-in file handler.");
}
}
// Default: read from a file.
std::vector<uint8_t> data;
size_t max_bytes = 1024 * 1024 * _max_asset_bytes_in_mb;
if (!io::ReadWholeFile(&data, err, resolvedPath, max_bytes,
/* userdata */ nullptr)) {
if (err) {
(*err) += "Open asset from a file failed.\n";
}
return false;
}
asset_out->set_data(std::move(data));
return true;
}
} // namespace tinyusdz

View File

@ -0,0 +1,364 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022 - Present, Light Transport Entertainment, Inc.
//
// Asset Resolution utilities
// https://graphics.pixar.com/usd/release/api/ar_page_front.html
//
// To avoid a confusion with AR(Argumented Reality), we doesn't use abberation
// `ar`, `Ar` and `AR`. ;-)
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include "nonstd/optional.hpp"
#include "value-types.hh"
namespace tinyusdz {
///
/// Abstract class for asset(e.g. file, memory, uri, ...)
/// Similar to ArAsset in pxrUSD.
///
class Asset {
public:
size_t size() const { return buf_.size(); }
const uint8_t *data() const { return buf_.data(); }
uint8_t *data() { return buf_.data(); }
void resize(size_t sz) { buf_.resize(sz); }
void shrink_to_fit() { buf_.shrink_to_fit(); }
void set_data(const std::vector<uint8_t> &&rhs) {
buf_ = rhs;
}
void set_name(const std::string &name) {
name_ = name;
}
void set_resolved_name(const std::string &name) {
resolved_name_ = name;
}
const std::string &name() const {
return name_;
}
const std::string &resolved_name() const {
return resolved_name_;
}
void set_version(const std::string &version) {
version_ = version;
}
const std::string &version() const {
return version_;
}
private:
std::string version_; // optional.
std::string name_;
std::string resolved_name_;
std::vector<uint8_t> buf_;
};
struct ResolverAssetInfo {
std::string version;
std::string assetName;
// std::string repoPath; deprecated in pxrUSD Ar 2.0
value::Value resolverInfo;
};
///
/// For easier language bindings(e.g. for C), we use simple callback function
/// approach.
///
// Resolve asset.
//
// @param[in] asset_name Asset name or filepath
// @param[in] search_paths Search paths.
// @param[out] resolved_asset_name Resolved asset name.
// @param[out] err Error message.
// @param[inout] userdata Userdata.
//
// @return 0 upon success. -1 = asset cannot be resolved(not found). other negative value = error
typedef int (*FSResolveAsset)(const char *asset_name, const std::vector<std::string> &search_paths, std::string *resolved_asset_name,
std::string *err, void *userdata);
// @param[in] resolved_asset_name Resolved Asset name or filepath
// @param[out] nbytes Bytes of this asset.
// @param[out] err Error message.
// @param[inout] userdata Userdata.
//
// @return 0 upon success. negative value = error
typedef int (*FSSizeAsset)(const char *resolved_asset_name, uint64_t *nbytes,
std::string *err, void *userdata);
// @param[in] resolved_asset_name Resolved Asset name or filepath
// @param[in] req_nbytes Required bytes for output buffer.
// @param[out] out_buf Output buffer. Memory should be allocated before calling this functione(`req_nbytes` or more)
// @param[out] nbytes Read bytes. 0 <= nbytes <= `req_nbytes`
// @param[out] err Error message.
// @param[inout] userdata Userdata.
//
// @return 0 upon success. negative value = error
typedef int (*FSReadAsset)(const char *resolved_asset_name, uint64_t req_nbytes, uint8_t *out_buf,
uint64_t *nbytes, std::string *err, void *userdata);
// @param[in] asset_name Asset name or filepath(could be empty)
// @param[in] resolved_asset_name Resolved Asset name or filepath
// @param[in] buffer Data.
// @param[in] nbytes Data bytes.
// @param[out] err Error message.
// @param[inout] userdata Userdata.
//
// @return 0 upon success. negative value = error
typedef int (*FSWriteAsset)(const char *asset_name, const char *resolved_asset_name, const uint8_t *buffer,
const uint64_t nbytes, std::string *err, void *userdata);
struct AssetResolutionHandler {
FSResolveAsset resolve_fun{nullptr};
FSSizeAsset size_fun{nullptr};
FSReadAsset read_fun{nullptr};
FSWriteAsset write_fun{nullptr};
void *userdata{nullptr};
};
#if 0 // deprecated.
///
/// @param[in] path Path string to be resolved.
/// @param[in] assetInfo nullptr when no `assetInfo` assigned to this path.
/// @param[inout] userdata Userdata pointer passed by callee. could be nullptr
/// @param[out] resolvedPath Resolved Path string.
/// @param[out] err Error message.
//
typedef bool (*ResolvePathHandler)(const std::string &path,
const ResolverAssetInfo *assetInfo,
void *userdata, std::string *resolvedPath,
std::string *err);
#endif
class AssetResolutionResolver {
public:
AssetResolutionResolver() = default;
~AssetResolutionResolver() {}
AssetResolutionResolver(const AssetResolutionResolver &rhs) {
if (this != &rhs) {
//_resolve_path_handler = rhs._resolve_path_handler;
_asset_resolution_handlers = rhs._asset_resolution_handlers;
_userdata = rhs._userdata;
_search_paths = rhs._search_paths;
}
}
AssetResolutionResolver &operator=(const AssetResolutionResolver &rhs) {
if (this != &rhs) {
// _resolve_path_handler = rhs._resolve_path_handler;
_asset_resolution_handlers = rhs._asset_resolution_handlers;
_userdata = rhs._userdata;
_search_paths = rhs._search_paths;
}
return (*this);
}
AssetResolutionResolver &operator=(AssetResolutionResolver &&rhs) {
if (this != &rhs) {
//_resolve_path_handler = rhs._resolve_path_handler;
_asset_resolution_handlers = rhs._asset_resolution_handlers;
_userdata = rhs._userdata;
_search_paths = std::move(rhs._search_paths);
}
return (*this);
}
// TinyUSDZ does not provide global search paths at the moment.
// static void SetDefaultSearchPath(const std::vector<std::string> &p);
void set_search_paths(const std::vector<std::string> &paths) {
// TODO: Validate input paths.
_search_paths = paths;
}
void add_search_path(const std::string &path) {
_search_paths.push_back(path);
}
//
// Asset is first seeked from the current working path(directory) when the Asset's path is a relative path.
//
void set_current_working_path(const std::string &cwp) {
_current_working_path = cwp;
}
const std::string &current_working_path() const {
return _current_working_path;
}
const std::vector<std::string> &search_paths() const { return _search_paths; }
std::string search_paths_str() const;
///
/// Register user defined AssetResolution handler per file extension.
/// Default = use built-in file handler(FILE/ifstream)
/// This handler is used in resolve(), find() and open_asset()
///
void register_asset_resolution_handler(const std::string &ext_name, AssetResolutionHandler handler) {
if (ext_name.empty()) {
return;
}
_asset_resolution_handlers[ext_name] = handler;
}
bool unregister_asset_resolution_handler(const std::string &ext_name) {
if (_asset_resolution_handlers.count(ext_name)) {
_asset_resolution_handlers.erase(ext_name);
}
return false;
}
bool has_asset_resolution_handler(const std::string &ext_name) {
return _asset_resolution_handlers.count(ext_name);
}
#if 0
///
/// Register user defined asset path resolver.
/// Default = find file from search paths.
///
void register_resolve_path_handler(ResolvePathHandler handler) {
_resolve_path_handler = handler;
}
void unregister_resolve_path_handler() { _resolve_path_handler = nullptr; }
#endif
///
/// Check if input asset exists(do asset resolution inside the function).
///
/// @param[in] assetPath Asset path string(e.g. "bora.png",
/// "/mnt/c/sphere.usd")
///
bool find(const std::string &assetPath) const;
///
/// Resolve asset path and returns resolved path as string.
/// Returns empty string when the asset does not exit.
///
std::string resolve(const std::string &assetPath) const;
///
/// Open asset from the resolved Path.
///
/// @param[in] resolvedPath Resolved path(through `resolve()`)
/// @param[in] assetPath Asset path(could be empty)
/// @param[out] asset Asset.
/// @param[out] warn Warning.
/// @param[out] err Error message.
///
/// @return true upon success.
///
bool open_asset(const std::string &resolvedPath, const std::string &assetPath,
Asset *asset, std::string *warn, std::string *err) const;
void set_userdata(void *userdata) { _userdata = userdata; }
void *get_userdata() { return _userdata; }
const void *get_userdata() const { return _userdata; }
void set_max_asset_bytes_in_mb(size_t megabytes) {
if (megabytes > 0) {
_max_asset_bytes_in_mb = megabytes;
}
}
size_t get_max_asset_bytes_in_mb() const {
return _max_asset_bytes_in_mb;
}
private:
//ResolvePathHandler _resolve_path_handler{nullptr};
void *_userdata{nullptr};
std::string _current_working_path{"./"};
std::vector<std::string> _search_paths;
mutable size_t _max_asset_bytes_in_mb{1024*1024}; // default 1 TB
std::map<std::string, AssetResolutionHandler> _asset_resolution_handlers;
// TODO: Cache resolution result
// mutable _dirty{true};
// mutable std::map<std::string, std::string> _cached_resolved_paths;
};
// forward decl
class PrimSpec;
//
// Fileformat plugin(callback) interface.
// For fileformat which is used in `subLayers`, `reference` or `payload`.
//
// TinyUSDZ uses C++ callback interface for security.
// (On the contrary, pxrUSD uses `plugInfo.json` + dll).
//
// Texture image/Shader file(e.g. glsl) is not handled in this API.
// (Plese refer T.B.D. for texture/shader)
//
// TODO: Move to another header file?
// Check if given data is a expectected file format
//
// @param[in] asset Asset data.
// @param[out] warn Warning message
// @param[out] err Error message(when the fuction returns false)
// @param[inout] user_data Userdata. can be nullptr.
// @return true when the given data is expected file format.
typedef bool (*FileFormatCheckFunction)(const Asset &asset, std::string *warn, std::string *err, void *user_data);
// Read content of data into PrimSpec(metadatum, properties, primChildren/variantChildren).
//
// TODO: Use `Layer` instead of `PrimSpec`?
//
// @param[in] asset Asset data
// @param[inout] ps PrimSpec which references/payload this asset.
// @param[out] warn Warning message
// @param[out] err Error message(when the fuction returns false)
// @param[inout] user_data Userdata. can be nullptr.
//
// @return true when reading data succeeds.
//
typedef bool (*FileFormatReadFunction)(const Asset &asset, PrimSpec &ps/* inout */, std::string *warn, std::string *err, void *user_data);
// Write corresponding content of PrimSpec to a binary data
//
// @param[in] ps PrimSpec which refers this asset.
// @param[out] out_asset Output asset data.
// @param[out] warn Warning message
// @param[out] err Error message(when the fuction returns false)
// @param[inout] user_data Userdata. can be nullptr.
// @return true upon data write success.
typedef bool (*FileFormatWriteFunction)(const PrimSpec &ps, Asset *out_data, std::string *warn, std::string *err, void *user_data);
struct FileFormatHandler
{
std::string extension; // fileformat extension.
std::string description; // Description of this fileformat. can be empty.
FileFormatCheckFunction checker{nullptr};
FileFormatReadFunction reader{nullptr};
FileFormatWriteFunction writer{nullptr};
void *userdata{nullptr};
};
} // namespace tinyusdz

View File

@ -0,0 +1,119 @@
#pragma once
#if !defined(TINYUSDZ_PRODUCTION_BUILD) && !defined(TINYUSDZ_FUZZER_BUILD)
#if defined(TINYUSDZ_DEBUG_PRINT)
#define TINYUSDZ_LOCAL_DEBUG_PRINT
#endif
#endif
#if defined(TINYUSDZ_PRODUCTION_BUILD)
// Do not include full filepath for privacy.
#define PUSH_ERROR_AND_RETURN(s) \
do { \
std::ostringstream ss_e; \
ss_e << "[error]" \
<< ":" << __func__ << "():" << __LINE__ << " "; \
ss_e << s << "\n"; \
PushError(ss_e.str()); \
return false; \
} while (0)
#define PUSH_ERROR_AND_RETURN_TAG(tag, s) \
do { \
std::ostringstream ss_e; \
ss_e << "[error]" << tag << ":" << __func__ << "():" << __LINE__ << " "; \
ss_e << s << "\n"; \
PushError(ss_e.str()); \
return false; \
} while (0)
#define PUSH_ERROR(s) \
do { \
std::ostringstream ss_e; \
ss_e << "[error]" \
<< ":" << __func__ << "():" << __LINE__ << " "; \
ss_e << s << "\n"; \
PushError(ss_e.str()); \
} while (0)
#define PUSH_WARN(s) \
do { \
std::ostringstream ss_w; \
ss_w << "[warn]" \
<< ":" << __func__ << "():" << __LINE__ << " "; \
ss_w << s << "\n"; \
PushWarn(ss_w.str()); \
} while (0)
#else // TINYUSDZ_PRODUCTION_BUILD
#define PUSH_ERROR_AND_RETURN(s) \
do { \
std::ostringstream ss_e; \
ss_e << "[error]" << __FILE__ << ":" << __func__ << "():" << __LINE__ \
<< " "; \
ss_e << s << "\n"; \
PushError(ss_e.str()); \
return false; \
} while (0)
#define PUSH_ERROR_AND_RETURN_TAG(tag, s) \
do { \
std::ostringstream ss_e; \
ss_e << "[error]" << __FILE__ << tag << ":" << __func__ \
<< "():" << __LINE__ << " "; \
ss_e << s << "\n"; \
PushError(ss_e.str()); \
return false; \
} while (0)
#define PUSH_ERROR(s) \
do { \
std::ostringstream ss_e; \
ss_e << "[error]" << __FILE__ << ":" << __func__ << "():" << __LINE__ \
<< " "; \
ss_e << s << "\n"; \
PushError(ss_e.str()); \
} while (0)
#define PUSH_WARN(s) \
do { \
std::ostringstream ss_w; \
ss_w << "[warn]" << __FILE__ << ":" << __func__ << "():" << __LINE__ \
<< " "; \
ss_w << s << "\n"; \
PushWarn(ss_w.str()); \
} while (0)
#endif // TINYUSDZ_PRODUCTION_BUILD
#if defined(TINYUSDZ_LOCAL_DEBUG_PRINT)
#define DCOUT(x) \
do { \
std::cout << __FILE__ << ":" << __func__ << ":" \
<< std::to_string(__LINE__) << " " << x << "\n"; \
} while (false)
#else
#define DCOUT(x)
#endif
// Simple auto-free class
// Use this class when saving stack size is required(e.g. recursive function call).
// T must have default constructor
template<typename T>
class AutoFree {
private:
T *_v{nullptr};
public:
AutoFree() : _v(new T()) {
}
~AutoFree() {
delete _v;
}
T &value() { return *_v; }
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,305 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022-Present Light Transport Entertainment Inc.
//
// Layer and Prim composition features.
//
#pragma once
#include "asset-resolution.hh"
#include "prim-types.hh"
// TODO
// - [x] Compose `references`
// - [x] Compose `payloads`
// - [ ] Compose `specializes`
// - [x] Compose `inherits`
// - [ ] Compose `variantSets`
// - [x] Compose `over`
// - [ ] Consider `active` Prim metadatum
namespace tinyusdz {
// Forward decl.
class Stage;
// USD asset loading state.
enum class LoadState : uint32_t {
Toplevel = 1, // load initial .usd(default)
Sublayer = 1 << 1, // load USD from Stage meta sublayer
Reference = 1 << 2, // load USD from Prim meta reference
Payload = 1 << 3 // load USD from Prim meta payload
};
struct SublayersCompositionOptions {
// The maximum depth for nested `subLayers`
uint32_t max_depth = 1024u;
// Make an error when referenced asset does not contain prims.
bool error_when_no_prims_in_sublayer{false};
// Make an error when referenced asset is not found
bool error_when_asset_not_found{false};
// Make an error when referenced asset is unsupported(e.g. unknown file extension)
bool error_when_unsupported_fileformat{false};
// File formats
std::map<std::string, FileFormatHandler> fileformats;
};
struct ReferencesCompositionOptions {
// The maximum depth for nested `references`
uint32_t max_depth = 1024u;
// Make an error when referenced asset is not found
bool error_when_asset_not_found{false};
// Make an error when referenced asset is unsupported(e.g. unknown file extension)
bool error_when_unsupported_fileformat{false};
// File formats
std::map<std::string, FileFormatHandler> fileformats;
};
struct PayloadCompositionOptions {
// The maximum depth for nested `payload`
uint32_t max_depth = 1024u;
// Make an error when referenced asset is not found
bool error_when_asset_not_found{false};
// Make an error when referenced asset is unsupported(e.g. unknown file extension)
bool error_when_unsupported_fileformat{false};
// File formats
std::map<std::string, FileFormatHandler> fileformats;
};
///
/// Return true when any PrimSpec in the Layer contains `references` Prim metadataum
///
/// Layer has cached flag for quicky detecting whether Layer has unresolved `references` or not.
///
/// @param[in] layer Layer
/// @param[in] force_check When true, traverse PrimSpec hierarchy and find `references` metadatum. Use cached flag in `layer` when false.
///
bool HasReferences(const Layer &layer, const bool force_check = false, const ReferencesCompositionOptions options = ReferencesCompositionOptions());
///
/// Return true when any PrimSpec in the Layer contains `payload` Prim metadataum
///
/// Layer has cached flag for quicky detecting whether Layer has unresolved `payload` or not.
///
/// @param[in] layer Layer
/// @param[in] force_check When true, traverse PrimSpec hierarchy and find `payload` metadatum. Use cached flag in `layer` when false.
///
bool HasPayload(const Layer &layer, const bool force_check = false, const PayloadCompositionOptions options = PayloadCompositionOptions());
///
/// Return true when any PrimSpec in the Layer contains `specializes` Prim metadataum.
/// We think specializers are not intensively used, so no caching.
///
/// @param[in] layer Layer
///
bool HasSpecializes(const Layer &layer);
///
/// Return true when any PrimSpec in the Layer contains `inherits` Prim metadataum.
///
/// @param[in] layer Layer
///
bool HasInherits(const Layer &layer);
///
/// Return true when any PrimSpec in the Layer contains `over` Prim.
///
/// @param[in] layer Layer
///
bool HasOver(const Layer &layer);
#if 0 // deprecate it.
///
/// Load subLayer USD files in `layer`, and return composited(flattened) Layer
/// to `composited_layer` Supply search_path with `base_dir`
///
bool CompositeSublayers(
const std::string &base_dir, const Layer &layer, Layer *composited_layer,
std::string *warn, std::string *err,
const SublayersCompositionOptions options = SublayersCompositionOptions());
#endif
///
/// Load subLayer USD files in `layer`, and return composited(flattened) Layer
/// to `composited_layer` Supply AssetResolutionResolver
///
bool CompositeSublayers(
AssetResolutionResolver &resolver /* inout */, const Layer &layer,
Layer *composited_layer, std::string *warn, std::string *err,
const SublayersCompositionOptions options = SublayersCompositionOptions());
///
/// Resolve `references` for each PrimSpe, and return composited(flattened)
/// Layer to `composited_layer` in `layer`.
///
bool CompositeReferences(AssetResolutionResolver &resolver /* inout */,
const Layer &layer, Layer *composited_layer,
std::string *warn, std::string *err,
const ReferencesCompositionOptions options =
ReferencesCompositionOptions());
///
/// Resolve `payload` for each PrimSpec, and return composited(flattened) Layer
/// to `composited_layer` in `layer`.
///
bool CompositePayload(
AssetResolutionResolver &resolver /* inout */, const Layer &layer,
Layer *composited_layer, std::string *warn, std::string *err,
const PayloadCompositionOptions options = PayloadCompositionOptions());
///
/// Resolve `variantSet` for each PrimSpec, and return composited(flattened) Layer
/// to `composited_layer` in `layer`.
/// Use variant selection info in each PrimSpec.
/// To externally specify variants to select, Use `ApplyVariantSelector`.
///
bool CompositeVariant(
const Layer &layer,
Layer *composited_layer, std::string *warn, std::string *err);
///
/// Resolve `specializes` for each PrimSpec, and return composited(flattened) Layer
/// to `composited_layer` in `layer`.
///
bool CompositeSpecializes(const Layer &layer,
Layer *composited_layer, std::string *warn, std::string *err);
///
/// Resolve `inherits` for each PrimSpec, and return composited(flattened) Layer
/// to `composited_layer` in `layer`.
///
bool CompositeInherits(const Layer &layer,
Layer *composited_layer, std::string *warn, std::string *err);
///
/// Override a PrimSpec with another PrimSpec.
///
/// @param[inout] dst PrimSpec to be override(must be `def` or `class` spec)
/// @param[in] src PrimSpec for override(must be `over` spec)
///
/// @return true upon success. false when error.
///
bool OverridePrimSpec(PrimSpec &dst, const PrimSpec &src, std::string *warn,
std::string *err);
///
/// Inherit PrimSpec. All PrimSpec tree in `src` PrimSpec will be inheritated to
/// `dst` PrimSpec.
///
/// @param[inout] dst PrimSpec to be inheritated
/// @param[in] src Source PrimSpec. Source PrimSpec can be any specifier(i.e,
/// `class`, `def` or `over`), but `class` recommended.
///
/// @return true upon success. false when error.
///
bool InheritPrimSpec(PrimSpec &dst, const PrimSpec &src, std::string *warn,
std::string *err);
///
/// Build USD Stage from Layer
///
bool LayerToStage(const Layer &layer, Stage *stage, std::string *warn,
std::string *err);
///
/// Build USD Stage from Layer
///
/// `layer` object will be destroyed after `stage` is being build.
///
bool LayerToStage(Layer &&layer, Stage *stage, std::string *warn,
std::string *err);
struct VariantSelector {
std::string selection; // current selection
VariantSelectionMap vsmap;
};
using VariantSelectorMap = std::map<Path, VariantSelector>;
///
/// Recursively traverse PrimSpec tree and collect variantSelection information.
///
/// key : PrimSpec path(e.g. "/root/xform0")
/// value : VariantSelectionInfo
///
/// TODO: Move to Tydra API?
///
bool ListVariantSelectionMaps(const Layer &layer, VariantSelectorMap &m);
///
/// Select variant(PrimSpec subtree) `variant_name` from `src` PrimSpec and
/// write it to `dst` PrimSpec.
///
/// @param[inout] dst PrimSpec where selected variant are written.
/// @param[in] src Source PrimSpec. Source PrimSpec.
/// @param[in] variant_selection Variant Selection list. key = variantSet name, value = variant name. Can be empty(when empty, use PrimSpec's variants information)
///
/// @return true upon success. false when error. No error when any of variant info in `variant_selection` does not exist in `src` PrimSpec.
///
bool VariantSelectPrimSpec(PrimSpec &dst, const PrimSpec &src,
const std::map<std::string, std::string> &variant_selection, std::string *warn,
std::string *err);
///
/// Resolve variant in PrimSpec tree and write result to `dst`.
/// `dst` does not contain any variant info.
///
bool ApplyVariantSelector(const Layer &layer, const VariantSelectorMap &vsmap,
Layer *dst, std::string *warn, std::string *err);
///
/// Handy version of ApplyVariantSelector.
/// Use same variant name for all variantSets in Prim tree.
///
bool ApplyVariantSelector(const Layer &layer, const std::string &variant_name,
Layer *dst, std::string *warn, std::string *err);
///
/// Implementation of `references`
///
/// Import `layer` to this PrimSpec.
///
/// @param[inout] dst PrimSpec to be referenced.
/// @param[in] layer Layer(PrimSpec tree) to reference.
/// @param[in] primPath root Prim path in `layer`. Default = invalid Path =
/// defaultPrim in `layer`.
/// @param[in] layerOffset Layer(PrimSpec tree) to reference.
///
/// Use `defaultPrim` in `layer` as the root PrimSpec to import
///
///
bool ReferenceLayerToPrimSpec(PrimSpec &dst, const Layer &layer,
const Path primPath = Path(),
const LayerOffset layerOffset = LayerOffset());
///
/// Extract Variant information from Layer.
///
/// Example:
///
/// { "/cube0" : { "variantSets" : ["colorVariant"], "variants" : { "colorVariant" : "green" } } }
///
bool ExtractVariants(const Layer &layer, Dictionary *dict, std::string *err);
///
/// Extract Variant information from Stage.
///
bool ExtractVariants(const Stage &stage, Dictionary *dict, std::string *err);
#if 0 // TODO
///
/// Implementation of `references`
///
bool ReferenceLayersToPrimSpec(PrimSpec &dst, const std::vector<Layer> &layers
#endif
} // namespace tinyusdz

View File

@ -0,0 +1,352 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022 - 2023, Syoyo Fujita.
// Copyright 2023 - Present, Light Transport Entertainment Inc.
#if defined(__wasi__)
#else
#include <thread>
#endif
#include "common-macros.inc"
#include "crate-format.hh"
#include "external/mapbox/eternal/include/mapbox/eternal.hpp"
#include "pprinter.hh"
#include "value-types.hh"
namespace tinyusdz {
namespace crate {
#if 0
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#endif
nonstd::expected<CrateDataType, std::string> GetCrateDataType(int32_t type_id) {
// TODO: Compile-time map using maxbox/eternal
static std::map<uint32_t, CrateDataType> table;
DCOUT("type_id = " << type_id);
if (table.size() == 0) {
// Register data types
// See <pxrUSD>/pxr/usd/usd/crateDataTypes.h
#define ADD_VALUE_TYPE(NAME_STR, TYPE_ID, SUPPORTS_ARRAY) \
{ \
assert(table.count(static_cast<uint32_t>(TYPE_ID)) == 0); \
table[static_cast<uint32_t>(TYPE_ID)] = \
CrateDataType(NAME_STR, TYPE_ID, SUPPORTS_ARRAY); \
}
// (num_string, type_id(in crateData), supports_array)
// 0 is reserved as `Invalid` type.
ADD_VALUE_TYPE("Invald", CrateDataTypeId::CRATE_DATA_TYPE_INVALID, false)
// Array types.
ADD_VALUE_TYPE("Bool", CrateDataTypeId::CRATE_DATA_TYPE_BOOL, true)
ADD_VALUE_TYPE("UChar", CrateDataTypeId::CRATE_DATA_TYPE_UCHAR, true)
ADD_VALUE_TYPE("Int", CrateDataTypeId::CRATE_DATA_TYPE_INT, true)
ADD_VALUE_TYPE("UInt", CrateDataTypeId::CRATE_DATA_TYPE_UINT, true)
ADD_VALUE_TYPE("Int64", CrateDataTypeId::CRATE_DATA_TYPE_INT64, true)
ADD_VALUE_TYPE("UInt64", CrateDataTypeId::CRATE_DATA_TYPE_UINT64, true)
ADD_VALUE_TYPE("Half", CrateDataTypeId::CRATE_DATA_TYPE_HALF, true)
ADD_VALUE_TYPE("Float", CrateDataTypeId::CRATE_DATA_TYPE_FLOAT, true)
ADD_VALUE_TYPE("Double", CrateDataTypeId::CRATE_DATA_TYPE_DOUBLE, true)
ADD_VALUE_TYPE("String", CrateDataTypeId::CRATE_DATA_TYPE_STRING, true)
ADD_VALUE_TYPE("Token", CrateDataTypeId::CRATE_DATA_TYPE_TOKEN, true)
ADD_VALUE_TYPE("AssetPath", CrateDataTypeId::CRATE_DATA_TYPE_ASSET_PATH,
true)
ADD_VALUE_TYPE("Matrix2d", CrateDataTypeId::CRATE_DATA_TYPE_MATRIX2D, true)
ADD_VALUE_TYPE("Matrix3d", CrateDataTypeId::CRATE_DATA_TYPE_MATRIX3D, true)
ADD_VALUE_TYPE("Matrix4d", CrateDataTypeId::CRATE_DATA_TYPE_MATRIX4D, true)
ADD_VALUE_TYPE("Quatd", CrateDataTypeId::CRATE_DATA_TYPE_QUATD, true)
ADD_VALUE_TYPE("Quatf", CrateDataTypeId::CRATE_DATA_TYPE_QUATF, true)
ADD_VALUE_TYPE("Quath", CrateDataTypeId::CRATE_DATA_TYPE_QUATH, true)
ADD_VALUE_TYPE("Vec2d", CrateDataTypeId::CRATE_DATA_TYPE_VEC2D, true)
ADD_VALUE_TYPE("Vec2f", CrateDataTypeId::CRATE_DATA_TYPE_VEC2F, true)
ADD_VALUE_TYPE("Vec2h", CrateDataTypeId::CRATE_DATA_TYPE_VEC2H, true)
ADD_VALUE_TYPE("Vec2i", CrateDataTypeId::CRATE_DATA_TYPE_VEC2I, true)
ADD_VALUE_TYPE("Vec3d", CrateDataTypeId::CRATE_DATA_TYPE_VEC3D, true)
ADD_VALUE_TYPE("Vec3f", CrateDataTypeId::CRATE_DATA_TYPE_VEC3F, true)
ADD_VALUE_TYPE("Vec3h", CrateDataTypeId::CRATE_DATA_TYPE_VEC3H, true)
ADD_VALUE_TYPE("Vec3i", CrateDataTypeId::CRATE_DATA_TYPE_VEC3I, true)
ADD_VALUE_TYPE("Vec4d", CrateDataTypeId::CRATE_DATA_TYPE_VEC4D, true)
ADD_VALUE_TYPE("Vec4f", CrateDataTypeId::CRATE_DATA_TYPE_VEC4F, true)
ADD_VALUE_TYPE("Vec4h", CrateDataTypeId::CRATE_DATA_TYPE_VEC4H, true)
ADD_VALUE_TYPE("Vec4i", CrateDataTypeId::CRATE_DATA_TYPE_VEC4I, true)
// Non-array types.
//
// commented = TODO
//
ADD_VALUE_TYPE("Dictionary", CrateDataTypeId::CRATE_DATA_TYPE_DICTIONARY,
false)
ADD_VALUE_TYPE("TokenListOp",
CrateDataTypeId::CRATE_DATA_TYPE_TOKEN_LIST_OP, false)
ADD_VALUE_TYPE("StringListOp",
CrateDataTypeId::CRATE_DATA_TYPE_STRING_LIST_OP, false)
ADD_VALUE_TYPE("PathListOp", CrateDataTypeId::CRATE_DATA_TYPE_PATH_LIST_OP,
false)
ADD_VALUE_TYPE("ReferenceListOp",
CrateDataTypeId::CRATE_DATA_TYPE_REFERENCE_LIST_OP, false)
ADD_VALUE_TYPE("IntListOp", CrateDataTypeId::CRATE_DATA_TYPE_INT_LIST_OP,
false)
ADD_VALUE_TYPE("Int64ListOp",
CrateDataTypeId::CRATE_DATA_TYPE_INT64_LIST_OP, false)
ADD_VALUE_TYPE("UIntListOp", CrateDataTypeId::CRATE_DATA_TYPE_UINT_LIST_OP,
false)
ADD_VALUE_TYPE("UInt64ListOp",
CrateDataTypeId::CRATE_DATA_TYPE_UINT64_LIST_OP, false)
ADD_VALUE_TYPE("PathVector", CrateDataTypeId::CRATE_DATA_TYPE_PATH_VECTOR,
false)
ADD_VALUE_TYPE("TokenVector", CrateDataTypeId::CRATE_DATA_TYPE_TOKEN_VECTOR,
false)
ADD_VALUE_TYPE("Specifier", CrateDataTypeId::CRATE_DATA_TYPE_SPECIFIER,
false)
ADD_VALUE_TYPE("Permission", CrateDataTypeId::CRATE_DATA_TYPE_PERMISSION,
false)
ADD_VALUE_TYPE("Variability", CrateDataTypeId::CRATE_DATA_TYPE_VARIABILITY,
false)
ADD_VALUE_TYPE("VariantSelectionMap",
CrateDataTypeId::CRATE_DATA_TYPE_VARIANT_SELECTION_MAP,
false)
ADD_VALUE_TYPE("TimeSamples", CrateDataTypeId::CRATE_DATA_TYPE_TIME_SAMPLES,
false)
ADD_VALUE_TYPE("Payload", CrateDataTypeId::CRATE_DATA_TYPE_PAYLOAD, false)
ADD_VALUE_TYPE("DoubleVector",
CrateDataTypeId::CRATE_DATA_TYPE_DOUBLE_VECTOR, false)
ADD_VALUE_TYPE("LayerOffsetVector",
CrateDataTypeId::CRATE_DATA_TYPE_LAYER_OFFSET_VECTOR, false)
ADD_VALUE_TYPE("StringVector",
CrateDataTypeId::CRATE_DATA_TYPE_STRING_VECTOR, false)
ADD_VALUE_TYPE("ValueBlock", CrateDataTypeId::CRATE_DATA_TYPE_VALUE_BLOCK,
false)
ADD_VALUE_TYPE("Value", CrateDataTypeId::CRATE_DATA_TYPE_VALUE, false)
ADD_VALUE_TYPE("UnregisteredValue",
CrateDataTypeId::CRATE_DATA_TYPE_UNREGISTERED_VALUE, false)
ADD_VALUE_TYPE("UnregisteredValueListOp",
CrateDataTypeId::CRATE_DATA_TYPE_UNREGISTERED_VALUE_LIST_OP,
false)
ADD_VALUE_TYPE("PayloadListOp",
CrateDataTypeId::CRATE_DATA_TYPE_PAYLOAD_LIST_OP, false)
ADD_VALUE_TYPE("TimeCode", CrateDataTypeId::CRATE_DATA_TYPE_TIME_CODE, true)
}
#undef ADD_VALUE_TYPE
if (type_id < 0) {
return nonstd::make_unexpected("Unknown type id: " +
std::to_string(type_id));
}
if (!table.count(static_cast<uint32_t>(type_id))) {
// Invalid or unsupported.
return nonstd::make_unexpected("Unknown or unspported type id: " +
std::to_string(type_id));
}
return table.at(static_cast<uint32_t>(type_id));
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#else
nonstd::expected<CrateDataType, std::string> GetCrateDataType(int32_t type_id) {
// See <pxrUSD>/pxr/usd/usd/crateDataTypes.h
// TODO: Use type name in value-types.hh and prim-types.hh?
MAPBOX_ETERNAL_CONSTEXPR const auto tymap =
mapbox::eternal::map<CrateDataTypeId, mapbox::eternal::string>({
{CrateDataTypeId::CRATE_DATA_TYPE_INVALID, "Invalid"}, // 0
{CrateDataTypeId::CRATE_DATA_TYPE_BOOL, "Bool"},
{CrateDataTypeId::CRATE_DATA_TYPE_UCHAR, "UChar"},
{CrateDataTypeId::CRATE_DATA_TYPE_INT, "Int"},
{CrateDataTypeId::CRATE_DATA_TYPE_UINT, "UInt"},
{CrateDataTypeId::CRATE_DATA_TYPE_INT64, "Int64"},
{CrateDataTypeId::CRATE_DATA_TYPE_UINT64, "UInt64"},
{CrateDataTypeId::CRATE_DATA_TYPE_HALF, "Half"},
{CrateDataTypeId::CRATE_DATA_TYPE_FLOAT, "Float"},
{CrateDataTypeId::CRATE_DATA_TYPE_DOUBLE, "Double"},
{CrateDataTypeId::CRATE_DATA_TYPE_STRING, "String"},
{CrateDataTypeId::CRATE_DATA_TYPE_TOKEN, "Token"},
{CrateDataTypeId::CRATE_DATA_TYPE_ASSET_PATH, "AssetPath"},
{CrateDataTypeId::CRATE_DATA_TYPE_MATRIX2D, "Matrix2d"},
{CrateDataTypeId::CRATE_DATA_TYPE_MATRIX3D, "Matrix3d"},
{CrateDataTypeId::CRATE_DATA_TYPE_MATRIX4D, "Matrix4d"},
{CrateDataTypeId::CRATE_DATA_TYPE_QUATD, "Quatd"},
{CrateDataTypeId::CRATE_DATA_TYPE_QUATF, "Quatf"},
{CrateDataTypeId::CRATE_DATA_TYPE_QUATH, "Quath"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC2D, "Vec2d"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC2F, "Vec2f"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC2H, "Vec2h"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC2I, "Vec2i"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC3D, "Vec3d"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC3F, "Vec3f"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC3H, "Vec3h"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC3I, "Vec3i"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC4D, "Vec4d"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC4F, "Vec4f"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC4H, "Vec4h"},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC4I, "Vec4i"},
// Non-array types.
{CrateDataTypeId::CRATE_DATA_TYPE_DICTIONARY, "Dictionary"},
{CrateDataTypeId::CRATE_DATA_TYPE_TOKEN_LIST_OP, "TokenListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_STRING_LIST_OP, "StringListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_PATH_LIST_OP, "PathListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_REFERENCE_LIST_OP,
"ReferenceListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_INT_LIST_OP, "IntListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_INT64_LIST_OP, "Int64ListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_UINT_LIST_OP, "UIntListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_UINT64_LIST_OP, "UInt64ListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_PATH_VECTOR, "PathVector"},
{CrateDataTypeId::CRATE_DATA_TYPE_TOKEN_VECTOR, "TokenVector"},
{CrateDataTypeId::CRATE_DATA_TYPE_SPECIFIER, "Specifier"},
{CrateDataTypeId::CRATE_DATA_TYPE_PERMISSION, "Permission"},
{CrateDataTypeId::CRATE_DATA_TYPE_VARIABILITY, "Variability"},
{CrateDataTypeId::CRATE_DATA_TYPE_VARIANT_SELECTION_MAP,
"VariantSelectionMap"},
{CrateDataTypeId::CRATE_DATA_TYPE_TIME_SAMPLES, "TimeSamples"},
{CrateDataTypeId::CRATE_DATA_TYPE_PAYLOAD, "Payload"},
{CrateDataTypeId::CRATE_DATA_TYPE_DOUBLE_VECTOR, "DoubleVector"},
{CrateDataTypeId::CRATE_DATA_TYPE_LAYER_OFFSET_VECTOR,
"LayerOffsetVector"},
{CrateDataTypeId::CRATE_DATA_TYPE_STRING_VECTOR, "StringVector"},
{CrateDataTypeId::CRATE_DATA_TYPE_VALUE_BLOCK, "ValueBlock"},
{CrateDataTypeId::CRATE_DATA_TYPE_VALUE, "Value"},
{CrateDataTypeId::CRATE_DATA_TYPE_UNREGISTERED_VALUE,
"UnregisteredValue"},
{CrateDataTypeId::CRATE_DATA_TYPE_UNREGISTERED_VALUE_LIST_OP,
"UnregisteredValueListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_PAYLOAD_LIST_OP, "PayloadListOp"},
{CrateDataTypeId::CRATE_DATA_TYPE_TIME_CODE, "TimeCode"},
});
// List up `supports array` type.
// TODO: Use compile-time `set`
MAPBOX_ETERNAL_CONSTEXPR const auto arrmap =
mapbox::eternal::map<CrateDataTypeId, bool>({
{CrateDataTypeId::CRATE_DATA_TYPE_BOOL, true},
{CrateDataTypeId::CRATE_DATA_TYPE_UCHAR, true},
{CrateDataTypeId::CRATE_DATA_TYPE_INT, true},
{CrateDataTypeId::CRATE_DATA_TYPE_UINT, true},
{CrateDataTypeId::CRATE_DATA_TYPE_INT64, true},
{CrateDataTypeId::CRATE_DATA_TYPE_UINT64, true},
{CrateDataTypeId::CRATE_DATA_TYPE_HALF, true},
{CrateDataTypeId::CRATE_DATA_TYPE_FLOAT, true},
{CrateDataTypeId::CRATE_DATA_TYPE_DOUBLE, true},
{CrateDataTypeId::CRATE_DATA_TYPE_STRING, true},
{CrateDataTypeId::CRATE_DATA_TYPE_TOKEN, true},
{CrateDataTypeId::CRATE_DATA_TYPE_ASSET_PATH, true},
{CrateDataTypeId::CRATE_DATA_TYPE_MATRIX2D, true},
{CrateDataTypeId::CRATE_DATA_TYPE_MATRIX3D, true},
{CrateDataTypeId::CRATE_DATA_TYPE_MATRIX4D, true},
{CrateDataTypeId::CRATE_DATA_TYPE_QUATD, true},
{CrateDataTypeId::CRATE_DATA_TYPE_QUATF, true},
{CrateDataTypeId::CRATE_DATA_TYPE_QUATH, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC2D, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC2F, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC2H, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC2I, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC3D, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC3F, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC3H, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC3I, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC4D, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC4F, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC4H, true},
{CrateDataTypeId::CRATE_DATA_TYPE_VEC4I, true},
{CrateDataTypeId::CRATE_DATA_TYPE_TIME_CODE, true},
});
if (type_id < 0) {
return nonstd::make_unexpected("Unknown type id: " +
std::to_string(type_id));
}
auto tyret = tymap.find(static_cast<CrateDataTypeId>(type_id));
if (tyret == tymap.end()) {
// Invalid or unsupported.
return nonstd::make_unexpected("Unknown or unspported type id: " +
std::to_string(type_id));
}
bool supports_array = arrmap.count(static_cast<CrateDataTypeId>(type_id));
CrateDataType dst(tyret->second.data(), static_cast<CrateDataTypeId>(type_id),
supports_array);
return std::move(dst);
}
#endif
std::string GetCrateDataTypeRepr(CrateDataType dty) {
auto tyRet = GetCrateDataType(static_cast<int32_t>(dty.dtype_id));
if (!tyRet) {
return "[Invalid]";
}
const CrateDataType ty = tyRet.value();
std::stringstream ss;
ss << "CrateDataType: " << ty.name << "("
<< static_cast<uint32_t>(ty.dtype_id)
<< "), supports_array = " << ty.supports_array;
return ss.str();
}
std::string GetCrateDataTypeName(int32_t type_id) {
auto tyRet = GetCrateDataType(type_id);
if (!tyRet) {
return "[Invalid]";
}
const CrateDataType dty = tyRet.value();
return dty.name;
}
std::string GetCrateDataTypeName(CrateDataTypeId did) {
return GetCrateDataTypeName(static_cast<int32_t>(did));
}
// std::string CrateValue::GetTypeName() const { return value_.type_name(); }
// uint32_t CrateValue::GetTypeId() const { return value_.type_id(); }
} // namespace crate
} // namespace tinyusdz

View File

@ -0,0 +1,530 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022 - 2023, Syoyo Fujita.
// Copyright 2023 - Present, Light Transport Entertainment Inc.
//
// USDC(CrateFile) format
#pragma once
#include <cstdint>
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <cstdlib>
#include "prim-types.hh"
#include "value-types.hh"
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
#include "nonstd/expected.hpp"
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
namespace tinyusdz {
namespace crate {
constexpr size_t kMinCompressedArraySize = 16;
constexpr size_t kSectionNameMaxLength = 15;
constexpr char kTokenVector[] = "TokenVector";
constexpr char kStringVector[] = "StringVector";
constexpr char kPathVector[] = "PathVector";
// crate data type
// id must be identitical to <pxrUSD>/pxr/usd/usd/crateDataType.h
enum class CrateDataTypeId {
CRATE_DATA_TYPE_INVALID = 0,
CRATE_DATA_TYPE_BOOL = 1,
CRATE_DATA_TYPE_UCHAR = 2,
CRATE_DATA_TYPE_INT = 3,
CRATE_DATA_TYPE_UINT = 4,
CRATE_DATA_TYPE_INT64 = 5,
CRATE_DATA_TYPE_UINT64 = 6,
CRATE_DATA_TYPE_HALF = 7,
CRATE_DATA_TYPE_FLOAT = 8,
CRATE_DATA_TYPE_DOUBLE = 9,
CRATE_DATA_TYPE_STRING = 10,
CRATE_DATA_TYPE_TOKEN = 11,
CRATE_DATA_TYPE_ASSET_PATH = 12,
CRATE_DATA_TYPE_MATRIX2D = 13,
CRATE_DATA_TYPE_MATRIX3D = 14,
CRATE_DATA_TYPE_MATRIX4D = 15,
CRATE_DATA_TYPE_QUATD = 16,
CRATE_DATA_TYPE_QUATF = 17,
CRATE_DATA_TYPE_QUATH = 18,
CRATE_DATA_TYPE_VEC2D = 19,
CRATE_DATA_TYPE_VEC2F = 20,
CRATE_DATA_TYPE_VEC2H = 21,
CRATE_DATA_TYPE_VEC2I = 22,
CRATE_DATA_TYPE_VEC3D = 23,
CRATE_DATA_TYPE_VEC3F = 24,
CRATE_DATA_TYPE_VEC3H = 25,
CRATE_DATA_TYPE_VEC3I = 26,
CRATE_DATA_TYPE_VEC4D = 27,
CRATE_DATA_TYPE_VEC4F = 28,
CRATE_DATA_TYPE_VEC4H = 29,
CRATE_DATA_TYPE_VEC4I = 30,
CRATE_DATA_TYPE_DICTIONARY = 31,
CRATE_DATA_TYPE_TOKEN_LIST_OP = 32,
CRATE_DATA_TYPE_STRING_LIST_OP = 33,
CRATE_DATA_TYPE_PATH_LIST_OP = 34,
CRATE_DATA_TYPE_REFERENCE_LIST_OP = 35,
CRATE_DATA_TYPE_INT_LIST_OP = 36,
CRATE_DATA_TYPE_INT64_LIST_OP = 37,
CRATE_DATA_TYPE_UINT_LIST_OP = 38,
CRATE_DATA_TYPE_UINT64_LIST_OP = 39,
CRATE_DATA_TYPE_PATH_VECTOR = 40,
CRATE_DATA_TYPE_TOKEN_VECTOR = 41,
CRATE_DATA_TYPE_SPECIFIER = 42,
CRATE_DATA_TYPE_PERMISSION = 43,
CRATE_DATA_TYPE_VARIABILITY = 44,
CRATE_DATA_TYPE_VARIANT_SELECTION_MAP = 45,
CRATE_DATA_TYPE_TIME_SAMPLES = 46,
CRATE_DATA_TYPE_PAYLOAD = 47,
CRATE_DATA_TYPE_DOUBLE_VECTOR = 48,
CRATE_DATA_TYPE_LAYER_OFFSET_VECTOR = 49,
CRATE_DATA_TYPE_STRING_VECTOR = 50,
CRATE_DATA_TYPE_VALUE_BLOCK = 51,
CRATE_DATA_TYPE_VALUE = 52, // Contains ValueRep
CRATE_DATA_TYPE_UNREGISTERED_VALUE = 53, // String or Dict
CRATE_DATA_TYPE_UNREGISTERED_VALUE_LIST_OP = 54,
CRATE_DATA_TYPE_PAYLOAD_LIST_OP = 55,
CRATE_DATA_TYPE_TIME_CODE = 56,
NumDataTypes // terminator
};
class CrateDataType
{
public:
CrateDataType() = default;
CrateDataType(const char *s, CrateDataTypeId did, bool a)
: name(s), dtype_id(did), supports_array(a) {
}
CrateDataType(const CrateDataType &rhs) = default;
CrateDataType &operator=(const CrateDataType&rhs) = default;
const char *name{nullptr}; // name of CrateDatatType. Constant symbol. TODO: Use string_view.
CrateDataTypeId dtype_id{CrateDataTypeId::CRATE_DATA_TYPE_INVALID};
bool supports_array{false};
};
std::string GetCrateDataTypeRepr(CrateDataType dty); // for debug cout
nonstd::expected<CrateDataType, std::string> GetCrateDataType(int32_t type_id);
std::string GetCrateDataTypeName(int32_t type_id);
std::string GetCrateDataTypeName(CrateDataTypeId type_id);
// -- from USD ----------------------------------------------------------------
//
// Copyright 2016 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
// Index base class. Used to index various tables. Deriving adds some
// type-safety so we don't accidentally use one kind of index with the wrong
// kind of table.
struct Index {
Index() : value(~0u) {}
explicit Index(uint32_t v) : value(v) {}
bool operator==(const Index &other) const { return value == other.value; }
bool operator!=(const Index &other) const { return !(*this == other); }
bool operator<(const Index &other) const { return value < other.value; }
uint32_t value;
};
// Value in file representation. Consists of a 2 bytes of type information
// (type enum value, array bit, and inlined-value bit) and 6 bytes of data.
// If possible, we attempt to store certain values directly in the local
// data, such as ints, floats, enums, and special-case values of other types
// (zero vectors, identity matrices, etc). For values that aren't stored
// inline, the 6 data bytes are the offset from the start of the file to the
// value's location.
struct ValueRep {
friend class CrateFile;
ValueRep() = default;
explicit constexpr ValueRep(uint64_t d) : data(d) {}
constexpr ValueRep(int32_t t, bool isInlined, bool isArray, uint64_t payload)
: data(Combine(t, isInlined, isArray, payload)) {}
static const uint64_t IsArrayBit_ = 1ull << 63;
static const uint64_t IsInlinedBit_ = 1ull << 62;
static const uint64_t IsCompressedBit_ = 1ull << 61;
static const uint64_t PayloadMask_ = ((1ull << 48) - 1);
inline bool IsArray() const { return data & IsArrayBit_; }
inline void SetIsArray() { data |= IsArrayBit_; }
inline bool IsInlined() const { return data & IsInlinedBit_; }
inline void SetIsInlined() { data |= IsInlinedBit_; }
inline bool IsCompressed() const { return data & IsCompressedBit_; }
inline void SetIsCompressed() { data |= IsCompressedBit_; }
inline int32_t GetType() const {
return static_cast<int32_t>((data >> 48) & 0xFF);
}
inline void SetType(int32_t t) {
data &= ~(0xFFull << 48); // clear type byte in data.
data |= (static_cast<uint64_t>(t) << 48); // set it.
}
inline uint64_t GetPayload() const { return data & PayloadMask_; }
inline void SetPayload(uint64_t payload) {
data &= ~PayloadMask_; // clear existing payload.
data |= payload & PayloadMask_;
}
inline uint64_t GetData() const { return data; }
bool operator==(ValueRep other) const { return data == other.data; }
bool operator!=(ValueRep other) const { return !(*this == other); }
// friend inline size_t hash_value(ValueRep v) {
// return static_cast<size_t>(v.data);
//}
std::string GetStringRepr() const {
std::stringstream ss;
ss << "ty: " << static_cast<int>(GetType()) << "(" << GetCrateDataTypeName(GetType()) << "), isArray: " << IsArray()
<< ", isInlined: " << IsInlined() << ", isCompressed: " << IsCompressed()
<< ", payload: " << GetPayload();
return ss.str();
}
private:
static constexpr uint64_t Combine(int32_t t, bool isInlined, bool isArray,
uint64_t payload) {
return (isArray ? IsArrayBit_ : 0) | (isInlined ? IsInlinedBit_ : 0) |
(static_cast<uint64_t>(t) << 48) | (payload & PayloadMask_);
}
uint64_t data;
};
struct TokenIndex : Index { using Index::Index; };
struct StringIndex : Index { using Index::Index; };
struct FieldIndex : Index { using Index::Index; };
struct FieldSetIndex : Index { using Index::Index; };
struct PathIndex : Index { using Index::Index; };
// ----------------------------------------------------------------------------
struct Field {
TokenIndex token_index;
ValueRep value_rep;
};
//
// Spec describes the relation of a path(i.e. node) and field(e.g. vertex data)
//
struct Spec {
Index path_index;
Index fieldset_index;
SpecType spec_type; // Must be 32bit
};
static_assert(sizeof(Spec) == (4 * 3), "sizeof(Spec) must be 12");
struct Section {
Section() { memset(this, 0, sizeof(*this)); }
Section(char const *name, int64_t start, int64_t size);
char name[kSectionNameMaxLength + 1];
int64_t start, size; // byte offset to section info and its data size
};
// For unordered_map
// https://stackoverflow.com/questions/8513911/how-to-create-a-good-hash-combine-with-64-bit-output-inspired-by-boosthash-co
// From CityHash code.
template <class T>
inline void hash_combine_impl32(std::size_t &seed, const T &v)
{
// Use boost version.
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
template <class T>
inline void hash_combine(std::size_t &seed, const T &v) {
#if defined(__wasi__) // 32bit platform
hash_combine_impl32(seed, v);
#else
if (sizeof(std::size_t) == 4) {
hash_combine_impl32(seed, v);
} else {
// Assume 64bit
std::hash<T> hasher;
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
uint64_t a = (hasher(v) ^ seed) * kMul;
a ^= (a >> 47);
uint64_t b = (seed ^ a) * kMul;
b ^= (b >> 47);
seed = size_t(b * kMul);
}
#endif
}
struct PathHasher {
size_t operator()(const Path &path) const {
size_t seed = std::hash<std::string>()(path.prim_part());
hash_combine(seed, std::hash<std::string>()(path.prop_part()));
//hash_combine(seed, std::hash<std::string>()(path.GetLocalPart()));
hash_combine(seed, std::hash<bool>()(path.is_valid()));
return seed;
}
};
struct PathKeyEqual {
bool operator()(const Path &lhs, const Path &rhs) const {
bool ret = lhs.prim_part() == rhs.prim_part();
ret &= lhs.prop_part() == rhs.prop_part();
//ret &= lhs.GetLocalPart() == rhs.GetLocalPart();
ret &= lhs.is_valid() == rhs.is_valid();
return ret;
}
};
struct FieldHasher {
size_t operator()(const Field &field) const {
size_t seed = std::hash<uint32_t>()(field.token_index.value);
hash_combine(seed, std::hash<uint64_t>()(field.value_rep.GetData()));
return seed;
}
};
struct FieldKeyEqual {
bool operator()(const Field &lhs, const Field &rhs) const {
bool ret = lhs.token_index == rhs.token_index;
ret &= lhs.value_rep == rhs.value_rep;
return ret;
}
};
struct FieldSetHasher {
size_t operator()(const std::vector<crate::FieldIndex> &fieldset) const {
if (fieldset.empty()) {
return 0;
}
size_t seed = std::hash<uint32_t>()(fieldset[0].value);
for (size_t i = 1; i < fieldset.size(); i++) {
hash_combine(seed, std::hash<uint32_t>()(fieldset[i].value));
}
return seed;
}
};
//
// TOC = list of sections.
//
struct TableOfContents {
// Section const *GetSection(SectionName) const;
// int64_t GetMinimumSectionStart() const;
std::vector<Section> sections;
};
// TODO: Use PrimVar?
class CrateValue {
public:
//typedef std::map<std::string, CrateValue> Dictionary;
//std::string GetTypeName() const;
//uint32_t GetTypeId() const;
#define SET_TYPE_SCALAR(__ty) void Set(const __ty& v) { value_ = v; }
#define SET_TYPE_1D(__ty) void Set(const std::vector<__ty> &v) { value_ = v; }
#define SET_TYPE_LIST(__FUNC) \
__FUNC(int64_t) \
__FUNC(uint64_t) \
__FUNC(value::half) \
__FUNC(value::half2) \
__FUNC(value::half3) \
__FUNC(value::half4) \
__FUNC(int) \
__FUNC(value::int2) \
__FUNC(value::int3) \
__FUNC(value::int4) \
__FUNC(uint32_t) \
__FUNC(value::uint2) \
__FUNC(value::uint3) \
__FUNC(value::uint4) \
__FUNC(float) \
__FUNC(value::float2) \
__FUNC(value::float3) \
__FUNC(value::float4) \
__FUNC(double) \
__FUNC(value::double2) \
__FUNC(value::double3) \
__FUNC(value::double4) \
__FUNC(value::quath) \
__FUNC(value::quatf) \
__FUNC(value::quatd) \
__FUNC(value::matrix2d) \
__FUNC(value::matrix3d) \
__FUNC(value::matrix4d) \
__FUNC(value::AssetPath) \
__FUNC(value::token) \
__FUNC(std::string)
// Note: Use bool and std::vector<bool> as-is in C++ layer, but its serialized as 8bit in Crate binary.
SET_TYPE_SCALAR(bool)
SET_TYPE_1D(bool)
SET_TYPE_SCALAR(Specifier)
SET_TYPE_SCALAR(Permission)
SET_TYPE_SCALAR(Variability)
SET_TYPE_SCALAR(value::dict)
SET_TYPE_SCALAR(value::ValueBlock)
SET_TYPE_SCALAR(ListOp<value::token>)
SET_TYPE_SCALAR(ListOp<std::string>)
SET_TYPE_SCALAR(ListOp<Path>)
SET_TYPE_SCALAR(ListOp<Reference>)
SET_TYPE_SCALAR(ListOp<int32_t>)
SET_TYPE_SCALAR(ListOp<uint32_t>)
SET_TYPE_SCALAR(ListOp<int64_t>)
SET_TYPE_SCALAR(ListOp<uint64_t>)
SET_TYPE_SCALAR(ListOp<Payload>)
SET_TYPE_SCALAR(std::vector<Path>)
// vector<double> is defined in SET_TYPE_LIST(SET_TYPE_1D)
//SET_TYPE_SCALAR(std::vector<double>)
SET_TYPE_SCALAR(std::vector<LayerOffset>)
SET_TYPE_SCALAR(Payload)
SET_TYPE_SCALAR(VariantSelectionMap)
SET_TYPE_SCALAR(value::TimeSamples)
SET_TYPE_SCALAR(CustomDataType) // for (type-restricted) dist
SET_TYPE_LIST(SET_TYPE_SCALAR)
SET_TYPE_LIST(SET_TYPE_1D)
#if 0 // TODO: Unsafe so Remove
// Useful function to retrieve concrete value with type T.
// Undefined behavior(usually will triger segmentation fault) when
// type-mismatch. (We don't throw exception)
template <class T>
const T value() const {
//return (*reinterpret_cast<const T *>(value_.value()));
//return linb::any_cast<const T>(value_);
return value_.value<T>();
}
#endif
// Type-safe way to get concrete value.
template <class T>
nonstd::optional<T> get_value() const {
return value_.get_value<T>();
}
// Return null when type-mismatch
template <class T>
const T *as() const {
return value_.as<T>();
}
std::string type_name() const {
return value_.type_name();
}
uint32_t type_id() const {
return value_.type_id();
}
const value::Value &get_raw() const {
return value_;
}
private:
value::Value value_;
};
// In-memory storage for a single "spec" -- prim, property, etc.
using FieldValuePair = std::pair<std::string, crate::CrateValue>;
using FieldValuePairVector = std::vector<FieldValuePair>;
struct StdHashWrapper {
template <class T>
inline size_t operator()(const T &val) const {
return std::hash<T>()(val);
}
};
} // namespace crate
namespace value {
#include "define-type-trait.inc"
// synonym to `value::dict`
//DEFINE_TYPE_TRAIT(crate::CrateValue::Dictionary, "dict", TYPE_ID_DICT, 1);
#undef DEFINE_TYPE_TRAIT
#undef DEFINE_ROLE_TYPE_TRAIT
} // namespace value
} // namespace tinyusdz

View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022 - 2023, Syoyo Fujita.
// Copyright 2023 - Present, Light Transport Entertainment Inc.
#include "crate-pprint.hh"
#include <string>
namespace std {
std::ostream &operator<<(std::ostream &os, const tinyusdz::crate::Index &i) {
os << std::to_string(i.value);
return os;
}
} // namespace std

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022 - 2023, Syoyo Fujita.
// Copyright 2023 - Present, Light Transport Entertainment Inc.
#pragma once
#include <iostream>
#include "crate-format.hh"
namespace std {
std::ostream &operator<<(std::ostream &os, const tinyusdz::crate::Index &i);
} // namespace std
namespace tinyusdz {
} // namespace tinyusdz

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,429 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2022 - 2023, Syoyo Fujita.
// Copyright 2023 - Present, Light Transport Entertainment Inc.
#pragma once
#include <string>
#include <unordered_set>
//
#include "nonstd/optional.hpp"
//
#include "crate-format.hh"
#include "prim-types.hh"
#include "stream-reader.hh"
namespace tinyusdz {
namespace crate {
// on: Use for-based PathIndex tree decoder to avoid potential buffer overflow(new implementation. its not well tested with fuzzer)
// off: Use recursive function call to decode PathIndex tree(its been working for a years and tested with fuzzer)
// TODO: After several battle-testing, make for-based PathIndex tree decoder default
#define TINYUSDZ_CRATE_USE_FOR_BASED_PATH_INDEX_DECODER
struct CrateReaderConfig {
int numThreads = -1;
// For malcious Crate data.
// Set limits to prevent infinite-loop, buffer-overrun, out-of-memory, etc.
size_t maxTOCSections = 32;
size_t maxNumTokens = 1024 * 1024 * 64;
size_t maxNumStrings = 1024 * 1024 * 64;
size_t maxNumFields = 1024 * 1024 * 256;
size_t maxNumFieldSets = 1024 * 1024 * 256;
size_t maxNumSpecifiers = 1024 * 1024 * 256;
size_t maxNumPaths = 1024 * 1024 * 256;
size_t maxNumIndices = 1024 * 1024 * 256;
size_t maxDictElements = 256;
size_t maxArrayElements = 1024 * 1024 * 1024; // 1G
size_t maxAssetPathElements = 512;
size_t maxTokenLength = 4096; // Maximum allowed length of `token` string
size_t maxStringLength = 1024 * 1024 * 64;
size_t maxVariantsMapElements = 128;
size_t maxValueRecursion = 16; // Prevent recursive Value unpack(e.g. Value encodes itself)
size_t maxPathIndicesDecodeIteration = 1024 * 1024 * 256; // Prevent infinite loop BuildDecompressedPathsImpl
// Generic int[] data
size_t maxInts = 1024 * 1024 * 1024;
// Total memory budget for uncompressed USD data(vertices, `tokens`, ...)` in
// [bytes].
size_t maxMemoryBudget = std::numeric_limits<int32_t>::max(); // Default 2GB
};
///
/// Crate(binary data) reader
///
class CrateReader {
public:
///
/// Intermediate Node data structure for scene graph.
/// This does not contain actual prim/property data.
///
class Node {
public:
// -2 = initialize as invalid node
Node() : _parent(-2) {}
Node(int64_t parent, Path &path) : _parent(parent), _path(path) {}
int64_t GetParent() const { return _parent; }
const std::vector<size_t> &GetChildren() const { return _children; }
///
/// child_name is used when reconstructing scene graph.
/// Return false when `child_name` is already added to a children.
///
bool AddChildren(const std::string &child_name, size_t node_index) {
if (_primChildren.count(child_name)) {
return false;
}
// assert(_primChildren.count(child_name) == 0);
_primChildren.emplace(child_name);
_children.push_back(node_index);
return true;
}
///
/// Get full path(e.g. `/muda/dora/bora` when the parent is `/muda/dora` and
/// this node is `bora`)
///
// std::string GetFullPath() const { return _path.full_path_name(); }
///
/// Get local path
///
std::string GetLocalPath() const { return _path.full_path_name(); }
///
/// Element Path(= name of Prim. Tokens in `primChildren` field). Prim node
/// only.
///
void SetElementPath(Path &path) { _elemPath = path; }
nonstd::optional<std::string> GetElementName() const {
if (_elemPath.is_relative_path()) {
return _elemPath.full_path_name();
} else {
return nonstd::nullopt;
}
}
// Element path(e.g. `geom0`)
const Path &GetElementPath() const { return _elemPath; }
// Full path(e.g. `/root/geom0`
const Path &GetPath() const { return _path; }
// crate::CrateDataType GetNodeDataType() const { return _node_type; }
const std::unordered_set<std::string> &GetPrimChildren() const {
return _primChildren;
}
// void SetAssetInfo(const value::dict &dict) { _assetInfo = dict; }
// const value::dict &GetAssetInfo() const { return _assetInfo; }
private:
int64_t
_parent; // -1 = this node is the root node. -2 = invalid or leaf node
std::vector<size_t> _children; // index to child nodes.
std::unordered_set<std::string>
_primChildren; // List of name of child nodes
Path _path; // local path
// value::dict _assetInfo;
Path _elemPath;
// value::TypeId _node_type;
// NodeType _node_type;
};
public:
private:
CrateReader() = delete;
public:
CrateReader(StreamReader *sr,
const CrateReaderConfig &config = CrateReaderConfig());
~CrateReader();
bool ReadBootStrap();
bool ReadTOC();
///
/// Read TOC section
///
bool ReadSection(crate::Section *s);
// Read known sections
bool ReadPaths();
bool ReadTokens();
bool ReadStrings();
bool ReadFields();
bool ReadFieldSets();
bool ReadSpecs();
bool BuildLiveFieldSets();
std::string GetError();
std::string GetWarning();
// Approximated memory usage in [mb]
size_t GetMemoryUsageInMB() const {
return size_t(_memoryUsage / 1024 / 1024);
}
/// -------------------------------------
/// Following Methods are valid after successfull parsing of Crate data.
///
size_t NumNodes() const { return _nodes.size(); }
const std::vector<Node> GetNodes() const { return _nodes; }
const std::vector<value::token> GetTokens() const { return _tokens; }
const std::vector<crate::Index> GetStringIndices() const {
return _string_indices;
}
const std::vector<crate::Field> &GetFields() const { return _fields; }
const std::vector<crate::Index> &GetFieldsetIndices() const {
return _fieldset_indices;
}
const std::vector<Path> &GetPaths() const { return _paths; }
const std::vector<Path> &GetElemPaths() const { return _elemPaths; }
const std::vector<crate::Spec> &GetSpecs() const { return _specs; }
const std::map<crate::Index, FieldValuePairVector> &GetLiveFieldSets() const {
return _live_fieldsets;
}
#if 0
// FIXME: May not need this
const std::vector<Path> &GetPaths() const {
return _paths;
}
#endif
const nonstd::optional<value::token> GetToken(crate::Index token_index) const;
const nonstd::optional<value::token> GetStringToken(
crate::Index string_index) const;
bool HasField(const std::string &key) const;
nonstd::optional<crate::Field> GetField(crate::Index index) const;
nonstd::optional<std::string> GetFieldString(crate::Index index) const;
nonstd::optional<std::string> GetSpecString(crate::Index index) const;
size_t NumPaths() const { return _paths.size(); }
nonstd::optional<Path> GetPath(crate::Index index) const;
nonstd::optional<Path> GetElementPath(crate::Index index) const;
nonstd::optional<std::string> GetPathString(crate::Index index) const;
///
/// Find if a field with (`name`, `tyname`) exists in FieldValuePairVector.
///
bool HasFieldValuePair(const FieldValuePairVector &fvs,
const std::string &name, const std::string &tyname);
///
/// Find if a field with `name`(type can be arbitrary) exists in
/// FieldValuePairVector.
///
bool HasFieldValuePair(const FieldValuePairVector &fvs,
const std::string &name);
nonstd::expected<FieldValuePair, std::string> GetFieldValuePair(
const FieldValuePairVector &fvs, const std::string &name,
const std::string &tyname);
nonstd::expected<FieldValuePair, std::string> GetFieldValuePair(
const FieldValuePairVector &fvs, const std::string &name);
// bool ParseAttribute(const FieldValuePairVector &fvs,
// PrimAttrib *attr,
// const std::string &prop_name);
bool VersionGreaterThanOrEqualTo_0_8_0() const {
if (_version[0] > 0) {
return true;
}
if (_version[1] >= 8) {
return true;
}
return false;
}
private:
#if defined(TINYUSDZ_CRATE_USE_FOR_BASED_PATH_INDEX_DECODER)
// To save stack usage
struct BuildDecompressedPathsArg {
std::vector<uint32_t> *pathIndexes{};
std::vector<int32_t> *elementTokenIndexes{};
std::vector<int32_t> *jumps{};
std::vector<bool> *visit_table{};
size_t startIndex{}; // usually 0
size_t endIndex{}; // inclusive. usually pathIndexes.size() - 1
Path parentPath;
};
bool BuildDecompressedPathsImpl(
BuildDecompressedPathsArg *arg);
#else
bool BuildDecompressedPathsImpl(
std::vector<uint32_t> const &pathIndexes,
std::vector<int32_t> const &elementTokenIndexes,
std::vector<int32_t> const &jumps,
std::vector<bool> &visit_table, // track visited pathIndex to prevent
// circular referencing
size_t curIndex, const Path &parentPath);
#endif
bool UnpackValueRep(const crate::ValueRep &rep, crate::CrateValue *value);
bool UnpackInlinedValueRep(const crate::ValueRep &rep,
crate::CrateValue *value);
//
// Construct node hierarchy.
//
bool BuildNodeHierarchy(
std::vector<uint32_t> const &pathIndexes,
std::vector<int32_t> const &elementTokenIndexes,
std::vector<int32_t> const &jumps,
std::vector<bool> &visit_table, // track visited pathIndex to prevent
// circular referencing
size_t curIndex, int64_t parentNodeIndex);
bool ReadCompressedPaths(const uint64_t ref_num_paths);
template <class Int>
bool ReadCompressedInts(Int *out, size_t num_elements);
bool ReadIndices(std::vector<crate::Index> *is);
bool ReadIndex(crate::Index *i);
bool ReadString(std::string *s);
bool ReadValueRep(crate::ValueRep *rep);
bool ReadPathArray(std::vector<Path> *d);
bool ReadStringArray(std::vector<std::string> *d);
bool ReadLayerOffsetArray(std::vector<LayerOffset> *d);
bool ReadReference(Reference *d);
bool ReadPayload(Payload *d);
bool ReadLayerOffset(LayerOffset *d);
// customData(Dictionary)
bool ReadCustomData(CustomDataType *d);
bool ReadTimeSamples(value::TimeSamples *d);
// integral array
template <typename T>
bool ReadIntArray(bool is_compressed, std::vector<T> *d);
bool ReadHalfArray(bool is_compressed, std::vector<value::half> *d);
bool ReadFloatArray(bool is_compressed, std::vector<float> *d);
bool ReadDoubleArray(bool is_compressed, std::vector<double> *d);
// template <class T>
// struct IsIntType {
// static const bool value =
// std::is_same<T, int32_t>::value ||
// std::is_same<T, uint32_t>::value ||
// std::is_same<T, int64_t>::value ||
// std::is_same<T, uint64_t>::value;
// };
template <typename T>
bool ReadArray(std::vector<T> *d);
// template <typename T,
// typename std::enable_if<IsIntType<T>::value, bool>::type>
// bool ReadArray(std::vector<T> *d);
template <typename T>
bool ReadListOp(ListOp<T> *d);
// TODO: Templatize
bool ReadPathListOp(ListOp<Path> *d);
bool ReadTokenListOp(ListOp<value::token> *d);
bool ReadStringListOp(ListOp<std::string> *d);
// bool ReadIntListOp(ListOp<int32_t> *d);
// bool ReadUIntListOp(ListOp<uint32_t> *d);
// bool ReadInt64ListOp(ListOp<int64_t> *d);
// bool ReadUInt64ListOp(ListOp<uint64_t> *d);
// bool ReadReferenceListOp(ListOp<Reference> *d);
// bool ReadPayloadListOp(ListOp<Payload> *d);
bool ReadVariantSelectionMap(VariantSelectionMap *d);
// Read 64bit uint with range check
bool ReadNum(uint64_t &n, uint64_t maxnum);
// Header(bootstrap)
uint8_t _version[3] = {0, 0, 0};
crate::TableOfContents _toc;
int64_t _toc_offset{0};
// index to _toc.sections
int64_t _tokens_index{-1};
int64_t _paths_index{-1};
int64_t _strings_index{-1};
int64_t _fields_index{-1};
int64_t _fieldsets_index{-1};
int64_t _specs_index{-1};
std::vector<value::token> _tokens;
std::vector<crate::Index> _string_indices;
std::vector<crate::Field> _fields;
std::vector<crate::Index> _fieldset_indices;
std::vector<crate::Spec> _specs;
std::vector<Path> _paths;
std::vector<Path> _elemPaths;
std::vector<Node> _nodes; // [0] = root node
//
// `_live_fieldsets` contains unpacked value keyed by fieldset index.
// Used for reconstructing Scene object
// TODO(syoyo): Use unordered_map?
std::map<crate::Index, FieldValuePairVector>
_live_fieldsets; // <fieldset index, List of field with unpacked Values>
const StreamReader *_sr{};
void PushError(const std::string &s) const { _err += s; }
void PushWarn(const std::string &s) const { _warn += s; }
mutable std::string _err;
mutable std::string _warn;
// To prevent recursive Value unpack(The Value encodes itself)
std::unordered_set<uint64_t> unpackRecursionGuard;
CrateReaderConfig _config;
// Approximated uncompressed memory usage(vertices, `tokens`, ...) in bytes.
uint64_t _memoryUsage{0};
class Impl;
Impl *_impl;
};
} // namespace crate
} // namespace tinyusdz

View File

@ -0,0 +1,40 @@
// TODO: Use (inline) constexpr for C++17
// TODO: Deprecate ndim()
#define DEFINE_TYPE_TRAIT(__dty, __name, __tyid, __nc) \
template <> \
struct TypeTraits<__dty> { \
using value_type = __dty; \
using value_underlying_type = __dty; \
static constexpr uint32_t ndim() { return 0; } /* array dim */ \
static constexpr size_t size() { return sizeof(__dty); } \
static constexpr uint32_t ncomp() { \
return __nc; \
} /* the number of components(e.g. extent = 2) */ \
static constexpr uint32_t type_id() { return __tyid; } \
static constexpr uint32_t underlying_type_id() { return __tyid; } \
static std::string type_name() { return __name; } \
static std::string underlying_type_name() { return __name; } \
static bool is_role_type() { return false; } \
static bool is_array() { return type_id() & value::TYPE_ID_1D_ARRAY_BIT; } \
}
// `role` type. Requies underlying type.
#define DEFINE_ROLE_TYPE_TRAIT(__dty, __name, __tyid, __uty) \
template <> \
struct TypeTraits<__dty> { \
using value_type = __dty; \
using value_underlying_type = TypeTraits<__uty>::value_type; \
static constexpr uint32_t ndim() { return 0; } /* array dim */ \
static constexpr size_t size() { return TypeTraits<__uty>::size(); } \
static constexpr uint32_t ncomp() { return TypeTraits<__uty>::ncomp(); } \
static constexpr uint32_t type_id() { return __tyid; } \
static constexpr uint32_t underlying_type_id() { \
return TypeTraits<__uty>::type_id(); \
} \
static std::string type_name() { return __name; } \
static std::string underlying_type_name() { \
return TypeTraits<__uty>::type_name(); \
} \
static bool is_role_type() { return true; } \
static bool is_array() { return type_id() & value::TYPE_ID_1D_ARRAY_BIT; } \
}

View File

@ -0,0 +1,19 @@
Copyright (C) 2014 Milo Yip
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,419 @@
// SPDX-License-Identifier: MIT
// Copyright (C) 2014 Milo Yip
// https://github.com/miloyip/dtoa-benchmark
//
#pragma once
#include <cassert>
#include <cmath>
#include <limits>
//#if defined(_MSC_VER)
//#include "msinttypes/stdint.h"
//#include <intrin.h>
//#else
// cstdint should be available for VS2019 or later
#include <cstdint>
//#endif
// TINYUSDZ: TODO: Completely disable int128 feature for portablity?
#if defined(__GNUC__)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && \
defined(__x86_64__)
namespace gcc_ints
{
__extension__ typedef __int128 int128;
__extension__ typedef unsigned __int128 uint128;
} // namespace gcc_ints
#endif
#endif
#define UINT64_C2(h, l) ((static_cast<uint64_t>(h) << 32) | static_cast<uint64_t>(l))
struct DiyFp {
DiyFp() {}
DiyFp(uint64_t _f, int _e) : f(_f), e(_e) {}
DiyFp(double d) {
union {
double d;
uint64_t u64;
} u = { d };
int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize;
uint64_t significand = (u.u64 & kDpSignificandMask);
if (biased_e != 0) {
f = significand + kDpHiddenBit;
e = biased_e - kDpExponentBias;
}
else {
f = significand;
e = kDpMinExponent + 1;
}
}
DiyFp operator-(const DiyFp& rhs) const {
assert(e == rhs.e);
assert(f >= rhs.f);
return DiyFp(f - rhs.f, e);
}
DiyFp operator*(const DiyFp& rhs) const {
#if defined(_MSC_VER) && defined(_M_AMD64)
uint64_t h;
uint64_t l = _umul128(f, rhs.f, &h);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
gcc_ints::uint128 p = static_cast<gcc_ints::uint128>(f) * static_cast<gcc_ints::uint128>(rhs.f);
uint64_t h = p >> 64;
uint64_t l = static_cast<uint64_t>(p);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#else
const uint64_t M32 = 0xFFFFFFFF;
const uint64_t a = f >> 32;
const uint64_t b = f & M32;
const uint64_t c = rhs.f >> 32;
const uint64_t d = rhs.f & M32;
const uint64_t ac = a * c;
const uint64_t bc = b * c;
const uint64_t ad = a * d;
const uint64_t bd = b * d;
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
tmp += 1U << 31; /// mult_round
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
#endif
}
DiyFp Normalize() const {
#if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index;
_BitScanReverse64(&index, f);
return DiyFp(f << (63 - int(index)), e - (63 - int(index)));
#elif defined(__GNUC__)
int s = __builtin_clzll(f);
return DiyFp(f << s, e - s);
#else
DiyFp res = *this;
while (!(res.f & kDpHiddenBit)) {
res.f <<= 1;
res.e--;
}
res.f <<= (kDiySignificandSize - kDpSignificandSize - 1);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 1);
return res;
#endif
}
DiyFp NormalizeBoundary() const {
#if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index;
_BitScanReverse64(&index, f);
return DiyFp (f << (63 - int(index)), e - (63 - int(index)));
#else
DiyFp res = *this;
while (!(res.f & (kDpHiddenBit << 1))) {
res.f <<= 1;
res.e--;
}
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
return res;
#endif
}
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
*plus = pl;
*minus = mi;
}
static const int kDiySignificandSize = 64;
static const int kDpSignificandSize = 52;
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
static const int kDpMinExponent = -kDpExponentBias;
static const uint64_t kDpExponentMask = UINT64_C2(0x7FF00000, 0x00000000);
static const uint64_t kDpSignificandMask = UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
static const uint64_t kDpHiddenBit = UINT64_C2(0x00100000, 0x00000000);
uint64_t f;
int e;
};
inline DiyFp GetCachedPower(int e, int* K) {
// 10^-348, 10^-340, ..., 10^340
static const uint64_t kCachedPowers_F[] = {
UINT64_C2(0xfa8fd5a0, 0x081c0288), UINT64_C2(0xbaaee17f, 0xa23ebf76),
UINT64_C2(0x8b16fb20, 0x3055ac76), UINT64_C2(0xcf42894a, 0x5dce35ea),
UINT64_C2(0x9a6bb0aa, 0x55653b2d), UINT64_C2(0xe61acf03, 0x3d1a45df),
UINT64_C2(0xab70fe17, 0xc79ac6ca), UINT64_C2(0xff77b1fc, 0xbebcdc4f),
UINT64_C2(0xbe5691ef, 0x416bd60c), UINT64_C2(0x8dd01fad, 0x907ffc3c),
UINT64_C2(0xd3515c28, 0x31559a83), UINT64_C2(0x9d71ac8f, 0xada6c9b5),
UINT64_C2(0xea9c2277, 0x23ee8bcb), UINT64_C2(0xaecc4991, 0x4078536d),
UINT64_C2(0x823c1279, 0x5db6ce57), UINT64_C2(0xc2109436, 0x4dfb5637),
UINT64_C2(0x9096ea6f, 0x3848984f), UINT64_C2(0xd77485cb, 0x25823ac7),
UINT64_C2(0xa086cfcd, 0x97bf97f4), UINT64_C2(0xef340a98, 0x172aace5),
UINT64_C2(0xb23867fb, 0x2a35b28e), UINT64_C2(0x84c8d4df, 0xd2c63f3b),
UINT64_C2(0xc5dd4427, 0x1ad3cdba), UINT64_C2(0x936b9fce, 0xbb25c996),
UINT64_C2(0xdbac6c24, 0x7d62a584), UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
UINT64_C2(0xf3e2f893, 0xdec3f126), UINT64_C2(0xb5b5ada8, 0xaaff80b8),
UINT64_C2(0x87625f05, 0x6c7c4a8b), UINT64_C2(0xc9bcff60, 0x34c13053),
UINT64_C2(0x964e858c, 0x91ba2655), UINT64_C2(0xdff97724, 0x70297ebd),
UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), UINT64_C2(0xf8a95fcf, 0x88747d94),
UINT64_C2(0xb9447093, 0x8fa89bcf), UINT64_C2(0x8a08f0f8, 0xbf0f156b),
UINT64_C2(0xcdb02555, 0x653131b6), UINT64_C2(0x993fe2c6, 0xd07b7fac),
UINT64_C2(0xe45c10c4, 0x2a2b3b06), UINT64_C2(0xaa242499, 0x697392d3),
UINT64_C2(0xfd87b5f2, 0x8300ca0e), UINT64_C2(0xbce50864, 0x92111aeb),
UINT64_C2(0x8cbccc09, 0x6f5088cc), UINT64_C2(0xd1b71758, 0xe219652c),
UINT64_C2(0x9c400000, 0x00000000), UINT64_C2(0xe8d4a510, 0x00000000),
UINT64_C2(0xad78ebc5, 0xac620000), UINT64_C2(0x813f3978, 0xf8940984),
UINT64_C2(0xc097ce7b, 0xc90715b3), UINT64_C2(0x8f7e32ce, 0x7bea5c70),
UINT64_C2(0xd5d238a4, 0xabe98068), UINT64_C2(0x9f4f2726, 0x179a2245),
UINT64_C2(0xed63a231, 0xd4c4fb27), UINT64_C2(0xb0de6538, 0x8cc8ada8),
UINT64_C2(0x83c7088e, 0x1aab65db), UINT64_C2(0xc45d1df9, 0x42711d9a),
UINT64_C2(0x924d692c, 0xa61be758), UINT64_C2(0xda01ee64, 0x1a708dea),
UINT64_C2(0xa26da399, 0x9aef774a), UINT64_C2(0xf209787b, 0xb47d6b85),
UINT64_C2(0xb454e4a1, 0x79dd1877), UINT64_C2(0x865b8692, 0x5b9bc5c2),
UINT64_C2(0xc83553c5, 0xc8965d3d), UINT64_C2(0x952ab45c, 0xfa97a0b3),
UINT64_C2(0xde469fbd, 0x99a05fe3), UINT64_C2(0xa59bc234, 0xdb398c25),
UINT64_C2(0xf6c69a72, 0xa3989f5c), UINT64_C2(0xb7dcbf53, 0x54e9bece),
UINT64_C2(0x88fcf317, 0xf22241e2), UINT64_C2(0xcc20ce9b, 0xd35c78a5),
UINT64_C2(0x98165af3, 0x7b2153df), UINT64_C2(0xe2a0b5dc, 0x971f303a),
UINT64_C2(0xa8d9d153, 0x5ce3b396), UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
UINT64_C2(0xbb764c4c, 0xa7a44410), UINT64_C2(0x8bab8eef, 0xb6409c1a),
UINT64_C2(0xd01fef10, 0xa657842c), UINT64_C2(0x9b10a4e5, 0xe9913129),
UINT64_C2(0xe7109bfb, 0xa19c0c9d), UINT64_C2(0xac2820d9, 0x623bf429),
UINT64_C2(0x80444b5e, 0x7aa7cf85), UINT64_C2(0xbf21e440, 0x03acdd2d),
UINT64_C2(0x8e679c2f, 0x5e44ff8f), UINT64_C2(0xd433179d, 0x9c8cb841),
UINT64_C2(0x9e19db92, 0xb4e31ba9), UINT64_C2(0xeb96bf6e, 0xbadf77d9),
UINT64_C2(0xaf87023b, 0x9bf0ee6b)
};
static const int16_t kCachedPowers_E[] = {
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
907, 933, 960, 986, 1013, 1039, 1066
};
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
int k = static_cast<int>(dk);
if (dk - k > 0.0)
k++;
unsigned index = static_cast<unsigned>((k >> 3) + 1);
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
assert(index < sizeof(kCachedPowers_F) / sizeof(kCachedPowers_F[0]));
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
}
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
while (rest < wp_w && delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || /// closer
wp_w - rest > rest + ten_kappa - wp_w)) {
buffer[len - 1]--;
rest += ten_kappa;
}
}
inline unsigned CountDecimalDigit32(uint32_t n) {
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;
if (n < 10000) return 4;
if (n < 100000) return 5;
if (n < 1000000) return 6;
if (n < 10000000) return 7;
if (n < 100000000) return 8;
if (n < 1000000000) return 9;
return 10;
}
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
uint64_t p2 = Mp.f & (one.f - 1);
int kappa = static_cast<int>(CountDecimalDigit32(p1));
*len = 0;
while (kappa > 0) {
uint32_t d;
switch (kappa) {
case 10: d = p1 / 1000000000; p1 %= 1000000000; break;
case 9: d = p1 / 100000000; p1 %= 100000000; break;
case 8: d = p1 / 10000000; p1 %= 10000000; break;
case 7: d = p1 / 1000000; p1 %= 1000000; break;
case 6: d = p1 / 100000; p1 %= 100000; break;
case 5: d = p1 / 10000; p1 %= 10000; break;
case 4: d = p1 / 1000; p1 %= 1000; break;
case 3: d = p1 / 100; p1 %= 100; break;
case 2: d = p1 / 10; p1 %= 10; break;
case 1: d = p1; p1 = 0; break;
default:
#if defined(_MSC_VER)
__assume(0);
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
__builtin_unreachable();
#else
d = 0;
#endif
}
if (d || *len)
buffer[(*len)++] = '0' + static_cast<char>(d);
kappa--;
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
if (tmp <= delta) {
*K += kappa;
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
return;
}
}
// kappa = 0
for (;;) {
p2 *= 10;
delta *= 10;
char d = static_cast<char>(p2 >> -one.e);
if (d || *len)
buffer[(*len)++] = '0' + d;
p2 &= one.f - 1;
kappa--;
if (p2 < delta) {
*K += kappa;
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]);
return;
}
}
}
inline void Grisu2(double value, char* buffer, int* length, int* K) {
const DiyFp v(value);
DiyFp w_m, w_p;
v.NormalizedBoundaries(&w_m, &w_p);
const DiyFp c_mk = GetCachedPower(w_p.e, K);
const DiyFp W = v.Normalize() * c_mk;
DiyFp Wp = w_p * c_mk;
DiyFp Wm = w_m * c_mk;
Wm.f++;
Wp.f--;
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
}
inline const char* GetDigitsLut() {
static const char cDigitsLut[200] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'
};
return cDigitsLut;
}
inline void WriteExponent(int K, char* buffer) {
if (K < 0) {
*buffer++ = '-';
K = -K;
}
if (K >= 100) {
*buffer++ = '0' + static_cast<char>(K / 100);
K %= 100;
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else if (K >= 10) {
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else
*buffer++ = '0' + static_cast<char>(K);
*buffer = '\0';
}
inline void Prettify(char* buffer, int length, int k) {
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
if (length <= kk && kk <= 21) {
// 1234e7 -> 12340000000
for (int i = length; i < kk; i++)
buffer[i] = '0';
buffer[kk] = '.';
buffer[kk + 1] = '0';
buffer[kk + 2] = '\0';
}
else if (0 < kk && kk <= 21) {
// 1234e-2 -> 12.34
memmove(&buffer[kk + 1], &buffer[kk], size_t(length - kk));
buffer[kk] = '.';
buffer[length + 1] = '\0';
}
else if (-6 < kk && kk <= 0) {
// 1234e-6 -> 0.001234
const int offset = 2 - kk;
memmove(&buffer[offset], &buffer[0], size_t(length));
buffer[0] = '0';
buffer[1] = '.';
for (int i = 2; i < offset; i++)
buffer[i] = '0';
buffer[length + offset] = '\0';
}
else if (length == 1) {
// 1e30
buffer[1] = 'e';
WriteExponent(kk - 1, &buffer[2]);
}
else {
// 1234e30 -> 1.234e33
memmove(&buffer[2], &buffer[1], size_t(length - 1));
buffer[1] = '.';
buffer[length + 1] = 'e';
WriteExponent(kk - 1, &buffer[0 + length + 2]);
}
}
inline void dtoa_milo(double value, char* buffer) {
// Not handling NaN and inf
assert(!std::isnan(value));
assert(!std::isinf(value));
if (std::fabs(value) < std::numeric_limits<double>::epsilon()) {
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
buffer[3] = '\0';
}
else {
if (value < 0) {
*buffer++ = '-';
value = -value;
}
int length, K;
Grisu2(value, buffer, &length, &K);
Prettify(buffer, length, K);
}
}

View File

@ -0,0 +1,190 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2021 The fast_float authors
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.

View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,27 @@
MIT License
Copyright (c) 2021 The fast_float authors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,291 @@
## fast_float number parsing library: 4x faster than strtod
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fast_float.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:fast_float)
[![VS17-CI](https://github.com/fastfloat/fast_float/actions/workflows/vs17-ci.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/vs17-ci.yml)
[![Ubuntu 22.04 CI (GCC 11)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml)
The fast_float library provides fast header-only implementations for the C++ from_chars
functions for `float` and `double` types. These functions convert ASCII strings representing
decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries.
Specifically, `fast_float` provides the following two functions with a C++17-like syntax (the library itself only requires C++11):
```C++
from_chars_result from_chars(const char* first, const char* last, float& value, ...);
from_chars_result from_chars(const char* first, const char* last, double& value, ...);
```
The return type (`from_chars_result`) is defined as the struct:
```C++
struct from_chars_result {
const char* ptr;
std::errc ec;
};
```
It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
a locale-independent format equivalent to the C++17 from_chars function.
The resulting floating-point value is the closest floating-point values (using either float or double),
using the "round to even" convention for values that would otherwise fall right in-between two values.
That is, we provide exact parsing according to the IEEE standard.
Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
`ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
It will parse infinity and nan values.
Example:
``` C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::string input = "3.1416 xyz ";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
the type `fast_float::chars_format`. It is a bitset value: we check whether
`fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
to determine whether we allow the fixed point and scientific notation respectively.
The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification.
* The `from_chars` function does not skip leading white-space characters.
* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden.
* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers.
Furthermore, we have the following restrictions:
* We only support `float` and `double` types at this time.
* We only support the decimal format: we do not support hexadecimal strings.
* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value and the returned `ec` is set to `std::errc::result_out_of_range`.
We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`).
## C++20: compile-time evaluation (constexpr)
In C++20, you may use `fast_float::from_chars` to parse strings
at compile-time, as in the following example:
```C++
// consteval forces compile-time evaluation of the function in C++20.
consteval double parse(std::string_view input) {
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) { return -1.0; }
return result;
}
// This function should compile to a function which
// merely returns 3.1415.
constexpr double constexptest() {
return parse("3.1415 input");
}
```
## Non-ASCII Inputs
We also support UTF-16 and UTF-32 inputs, as well as ASCII/UTF-8, as in the following example:
``` C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::u16string input = u"3.1416 xyz ";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
## Using commas as decimal separator
The C++ standard stipulate that `from_chars` has to be locale-independent. In
particular, the decimal separator has to be the period (`.`). However,
some users still want to use the `fast_float` library with in a locale-dependent
manner. Using a separate function called `from_chars_advanced`, we allow the users
to pass a `parse_options` instance which contains a custom decimal separator (e.g.,
the comma). You may use it as follows.
```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
const std::string input = "3,1416 xyz ";
double result;
fast_float::parse_options options{fast_float::chars_format::general, ','};
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
You can parse delimited numbers:
```C++
const std::string input = "234532.3426362,7869234.9823,324562.645";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) {
// check error
}
// we have result == 234532.3426362.
if(answer.ptr[0] != ',') {
// unexpected delimiter
}
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if(answer.ec != std::errc()) {
// check error
}
// we have result == 7869234.9823.
if(answer.ptr[0] != ',') {
// unexpected delimiter
}
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if(answer.ec != std::errc()) {
// check error
}
// we have result == 324562.645.
```
## Relation With Other Work
The fast_float library is part of:
- GCC (as of version 12): the `from_chars` function in GCC relies on fast_float.
- [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser)
The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS).
The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
## References
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021.
- Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience 53 (7), 2023.
## Other programming languages
- [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`.
- [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`.
- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. It used for important systems such as [Jackson](https://github.com/FasterXML/jackson-core).
- [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
## Users
The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet).
## How fast is it?
It can parse random floating-point numbers at a speed of 1 GB/s on some systems. We find that it is often twice as fast as the best available competitor, and many times faster than many standard-library implementations.
<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
```
$ ./build/benchmarks/benchmark
# parsing random integers in the range [0,1)
volume = 2.09808 MB
netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s
doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s
strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s
abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
```
See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code.
## Video
[![Go Systems 2020](http://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](http://www.youtube.com/watch?v=AVXgvlMeIm4)<br />
## Using as a CMake dependency
This library is header-only by design. The CMake file provides the `fast_float` target
which is merely a pointer to the `include` directory.
If you drop the `fast_float` repository in your CMake project, you should be able to use
it in this manner:
```cmake
add_subdirectory(fast_float)
target_link_libraries(myprogram PUBLIC fast_float)
```
Or you may want to retrieve the dependency automatically if you have a sufficiently recent version of CMake (3.11 or better at least):
```cmake
FetchContent_Declare(
fast_float
GIT_REPOSITORY https://github.com/lemire/fast_float.git
GIT_TAG tags/v1.1.2
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(fast_float)
target_link_libraries(myprogram PUBLIC fast_float)
```
You should change the `GIT_TAG` line so that you recover the version you wish to use.
## Using as single header
The script `script/amalgamate.py` may be used to generate a single header
version of the library if so desired.
Just run the script from the root directory of this repository.
You can customize the license type and output file if desired as described in
the command line help.
You may directly download automatically generated single-header files:
https://github.com/fastfloat/fast_float/releases/download/v5.2.0/fast_float.h
## Credit
Though this work is inspired by many different people, this work benefited especially from exchanges with
Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided
invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits.
The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published
under the Apache 2.0 license.
## License
<sup>
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
2.0</a> or <a href="LICENSE-MIT">MIT license</a> or <a href="LICENSE-BOOST">BOOST license</a> .
</sup>
<br>
<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this repository by you, as defined in the Apache-2.0 license,
shall be triple licensed as above, without any additional terms or conditions.
</sub>

View File

@ -0,0 +1,417 @@
#ifndef FASTFLOAT_ASCII_NUMBER_H
#define FASTFLOAT_ASCII_NUMBER_H
#include <cctype>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <type_traits>
#include "float_common.h"
#ifdef FASTFLOAT_SSE2
#include <emmintrin.h>
#endif
#ifdef FASTFLOAT_NEON
#include <arm_neon.h>
#endif
namespace fast_float {
template <typename UC>
fastfloat_really_inline constexpr bool has_simd_opt() {
#ifdef FASTFLOAT_HAS_SIMD
return std::is_same<UC, char16_t>::value;
#else
return false;
#endif
}
// Next function can be micro-optimized, but compilers are entirely
// able to optimize it well.
template <typename UC>
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
return !(c > UC('9') || c < UC('0'));
}
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56
| (val & 0x00FF000000000000) >> 40
| (val & 0x0000FF0000000000) >> 24
| (val & 0x000000FF00000000) >> 8
| (val & 0x00000000FF000000) << 8
| (val & 0x0000000000FF0000) << 24
| (val & 0x000000000000FF00) << 40
| (val & 0x00000000000000FF) << 56;
}
// Read 8 UC into a u64. Truncates UC if not char.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t read8_to_u64(const UC *chars) {
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
uint64_t val = 0;
for(int i = 0; i < 8; ++i) {
val |= uint64_t(uint8_t(*chars)) << (i*8);
++chars;
}
return val;
}
uint64_t val;
::memcpy(&val, chars, sizeof(uint64_t));
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
return val;
}
#ifdef FASTFLOAT_SSE2
fastfloat_really_inline
uint64_t simd_read8_to_u64(const __m128i data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
const __m128i packed = _mm_packus_epi16(data, data);
#ifdef FASTFLOAT_64BIT
return uint64_t(_mm_cvtsi128_si64(packed));
#else
uint64_t value;
// Visual Studio + older versions of GCC don't support _mm_storeu_si64
_mm_storel_epi64(reinterpret_cast<__m128i*>(&value), packed);
return value;
#endif
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
fastfloat_really_inline
uint64_t simd_read8_to_u64(const char16_t* chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
return simd_read8_to_u64(_mm_loadu_si128(reinterpret_cast<const __m128i*>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
#elif defined(FASTFLOAT_NEON)
fastfloat_really_inline
uint64_t simd_read8_to_u64(const uint16x8_t data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
uint8x8_t utf8_packed = vmovn_u16(data);
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
fastfloat_really_inline
uint64_t simd_read8_to_u64(const char16_t* chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
return simd_read8_to_u64(vld1q_u16(reinterpret_cast<const uint16_t*>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS
}
#endif // FASTFLOAT_SSE2
// MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC>
#else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>())>
#endif
// dummy for compile
uint64_t simd_read8_to_u64(UC const*) {
return 0;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void write_u64(uint8_t *chars, uint64_t val) {
if (cpp20_and_in_constexpr()) {
for(int i = 0; i < 8; ++i) {
*chars = uint8_t(val);
val >>= 8;
++chars;
}
return;
}
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
::memcpy(chars, &val, sizeof(uint64_t));
}
// credit @aqrit
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint32_t parse_eight_digits_unrolled(uint64_t val) {
const uint64_t mask = 0x000000FF000000FF;
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
val -= 0x3030303030303030;
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
return uint32_t(val);
}
// Call this if chars are definitely 8 digits.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint32_t parse_eight_digits_unrolled(UC const * chars) noexcept {
if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
}
return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
}
// credit @aqrit
fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080));
}
#ifdef FASTFLOAT_HAS_SIMD
// Call this if chars might not be 8 digits.
// Using this style (instead of is_made_of_eight_digits_fast() then parse_eight_digits_unrolled())
// ensures we don't load SIMD registers twice.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool simd_parse_if_eight_digits_unrolled(const char16_t* chars, uint64_t& i) noexcept {
if (cpp20_and_in_constexpr()) {
return false;
}
#ifdef FASTFLOAT_SSE2
FASTFLOAT_SIMD_DISABLE_WARNINGS
const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(chars));
// (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html
const __m128i t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
const __m128i t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
if (_mm_movemask_epi8(t1) == 0) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true;
}
else return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS
#elif defined(FASTFLOAT_NEON)
FASTFLOAT_SIMD_DISABLE_WARNINGS
const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t*>(chars));
// (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html
const uint16x8_t t0 = vsubq_u16(data, vmovq_n_u16('0'));
const uint16x8_t mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
if (vminvq_u16(mask) == 0xFFFF) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true;
}
else return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS
#else
(void)chars; (void)i;
return false;
#endif // FASTFLOAT_SSE2
}
#endif // FASTFLOAT_HAS_SIMD
// MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC>
#else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>())>
#endif
// dummy for compile
bool simd_parse_if_eight_digits_unrolled(UC const*, uint64_t&) {
return 0;
}
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value)>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void loop_parse_if_eight_digits(const UC*& p, const UC* const pend, uint64_t& i) {
if (!has_simd_opt<UC>()) {
return;
}
while ((std::distance(p, pend) >= 8) && simd_parse_if_eight_digits_unrolled(p, i)) { // in rare cases, this will overflow, but that's ok
p += 8;
}
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void loop_parse_if_eight_digits(const char*& p, const char* const pend, uint64_t& i) {
// optimizes better than parse_if_eight_digits_unrolled() for UC = char.
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(read8_to_u64(p))) {
i = i * 100000000 + parse_eight_digits_unrolled(read8_to_u64(p)); // in rare cases, this will overflow, but that's ok
p += 8;
}
}
template <typename UC>
struct parsed_number_string_t {
int64_t exponent{0};
uint64_t mantissa{0};
UC const * lastmatch{nullptr};
bool negative{false};
bool valid{false};
bool too_many_digits{false};
// contains the range of the significant digits
span<const UC> integer{}; // non-nullable
span<const UC> fraction{}; // nullable
};
using byte_span = span<const char>;
using parsed_number_string = parsed_number_string_t<char>;
// Assuming that you use no more than 19 digits, this will
// parse an ASCII string.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, parse_options_t<UC> options) noexcept {
chars_format const fmt = options.format;
UC const decimal_point = options.decimal_point;
parsed_number_string_t<UC> answer;
answer.valid = false;
answer.too_many_digits = false;
answer.negative = (*p == UC('-'));
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if ((*p == UC('-')) || (*p == UC('+'))) {
#else
if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
#endif
++p;
if (p == pend) {
return answer;
}
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
return answer;
}
}
UC const * const start_digits = p;
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
while ((p != pend) && is_integer(*p)) {
// a multiplication by 10 is cheaper than an arbitrary integer
// multiplication
i = 10 * i +
uint64_t(*p - UC('0')); // might overflow, we will handle the overflow later
++p;
}
UC const * const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = span<const UC>(start_digits, size_t(digit_count));
int64_t exponent = 0;
if ((p != pend) && (*p == decimal_point)) {
++p;
UC const * before = p;
// can occur at most twice without overflowing, but let it occur more, since
// for integers with many digits, digit parsing is the primary bottleneck.
loop_parse_if_eight_digits(p, pend, i);
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0'));
++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
}
exponent = before - p;
answer.fraction = span<const UC>(before, size_t(p - before));
digit_count -= exponent;
}
// we must have encountered at least one integer!
if (digit_count == 0) {
return answer;
}
int64_t exp_number = 0; // explicit exponential part
if ((fmt & chars_format::scientific) && (p != pend) && ((UC('e') == *p) || (UC('E') == *p))) {
UC const * location_of_e = p;
++p;
bool neg_exp = false;
if ((p != pend) && (UC('-') == *p)) {
neg_exp = true;
++p;
} else if ((p != pend) && (UC('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p;
}
if ((p == pend) || !is_integer(*p)) {
if(!(fmt & chars_format::fixed)) {
// We are in error.
return answer;
}
// Otherwise, we will be ignoring the 'e'.
p = location_of_e;
} else {
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0'));
if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + digit;
}
++p;
}
if(neg_exp) { exp_number = - exp_number; }
exponent += exp_number;
}
} else {
// If it scientific and not fixed, we have to bail out.
if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
}
answer.lastmatch = p;
answer.valid = true;
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon.
//
// We can deal with up to 19 digits.
if (digit_count > 19) { // this is uncommon
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000.
UC const * start = start_digits;
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
if(*start == UC('0')) { digit_count --; }
start++;
}
if (digit_count > 19) {
answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows.
// We don't need to check if is_integer, since we use the
// pre-tokenized spans from above.
i = 0;
p = answer.integer.ptr;
UC const* int_end = p + answer.integer.len();
const uint64_t minimal_nineteen_digit_integer{ 1000000000000000000 };
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
++p;
}
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
exponent = end_of_integer_part - p + exp_number;
}
else { // We have a value with a fractional component.
p = answer.fraction.ptr;
UC const* frac_end = p + answer.fraction.len();
while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
++p;
}
exponent = answer.fraction.ptr - p + exp_number;
}
// We have now corrected both exponent and i, to a truncated value
}
}
answer.exponent = exponent;
answer.mantissa = i;
return answer;
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,617 @@
#ifndef FASTFLOAT_BIGINT_H
#define FASTFLOAT_BIGINT_H
#include <algorithm>
#include <cstdint>
#include <climits>
#include <cstring>
#include "float_common.h"
namespace fast_float {
// the limb width: we want efficient multiplication of double the bits in
// limb, or for 64-bit limbs, at least 64-bit multiplication where we can
// extract the high and low parts efficiently. this is every 64-bit
// architecture except for sparc, which emulates 128-bit multiplication.
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
// doing `8 * sizeof(limb)`.
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
#define FASTFLOAT_64BIT_LIMB 1
typedef uint64_t limb;
constexpr size_t limb_bits = 64;
#else
#define FASTFLOAT_32BIT_LIMB
typedef uint32_t limb;
constexpr size_t limb_bits = 32;
#endif
typedef span<limb> limb_span;
// number of bits in a bigint. this needs to be at least the number
// of bits required to store the largest bigint, which is
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
// ~3600 bits, so we round to 4000.
constexpr size_t bigint_bits = 4000;
constexpr size_t bigint_limbs = bigint_bits / limb_bits;
// vector-like type that is allocated on the stack. the entire
// buffer is pre-allocated, and only the length changes.
template <uint16_t size>
struct stackvec {
limb data[size];
// we never need more than 150 limbs
uint16_t length{0};
stackvec() = default;
stackvec(const stackvec &) = delete;
stackvec &operator=(const stackvec &) = delete;
stackvec(stackvec &&) = delete;
stackvec &operator=(stackvec &&other) = delete;
// create stack vector from existing limb span.
FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
FASTFLOAT_ASSERT(try_extend(s));
}
FASTFLOAT_CONSTEXPR14 limb& operator[](size_t index) noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index];
}
FASTFLOAT_CONSTEXPR14 const limb& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index];
}
// index from the end of the container
FASTFLOAT_CONSTEXPR14 const limb& rindex(size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
size_t rindex = length - index - 1;
return data[rindex];
}
// set the length, without bounds checking.
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
length = uint16_t(len);
}
constexpr size_t len() const noexcept {
return length;
}
constexpr bool is_empty() const noexcept {
return length == 0;
}
constexpr size_t capacity() const noexcept {
return size;
}
// append item to vector, without bounds checking
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
data[length] = value;
length++;
}
// append item to vector, returning if item was added
FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
if (len() < capacity()) {
push_unchecked(value);
return true;
} else {
return false;
}
}
// add items to the vector, from a span, without bounds checking
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
limb* ptr = data + length;
std::copy_n(s.ptr, s.len(), ptr);
set_len(len() + s.len());
}
// try to add items to the vector, returning if items were added
FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
if (len() + s.len() <= capacity()) {
extend_unchecked(s);
return true;
} else {
return false;
}
}
// resize the vector, without bounds checking
// if the new size is longer than the vector, assign value to each
// appended item.
FASTFLOAT_CONSTEXPR20
void resize_unchecked(size_t new_len, limb value) noexcept {
if (new_len > len()) {
size_t count = new_len - len();
limb* first = data + len();
limb* last = first + count;
::std::fill(first, last, value);
set_len(new_len);
} else {
set_len(new_len);
}
}
// try to resize the vector, returning if the vector was resized.
FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
if (new_len > capacity()) {
return false;
} else {
resize_unchecked(new_len, value);
return true;
}
}
// check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index
// is relative to the most significant limbs.
FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
while (index < len()) {
if (rindex(index) != 0) {
return true;
}
index++;
}
return false;
}
// normalize the big integer, so most-significant zero limbs are removed.
FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) {
length--;
}
}
};
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint64_t empty_hi64(bool& truncated) noexcept {
truncated = false;
return 0;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
truncated = false;
int shl = leading_zeroes(r0);
return r0 << shl;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
int shl = leading_zeroes(r0);
if (shl == 0) {
truncated = r1 != 0;
return r0;
} else {
int shr = 64 - shl;
truncated = (r1 << shl) != 0;
return (r0 << shl) | (r1 >> shr);
}
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
return uint64_hi64(r0, truncated);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
return uint64_hi64((x0 << 32) | x1, truncated);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
uint64_t x2 = r2;
return uint64_hi64(x0, (x1 << 32) | x2, truncated);
}
// add two small integers, checking for overflow.
// we want an efficient operation. for msvc, where
// we don't have built-in intrinsics, this is still
// pretty fast.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
limb scalar_add(limb x, limb y, bool& overflow) noexcept {
limb z;
// gcc and clang
#if defined(__has_builtin)
#if __has_builtin(__builtin_add_overflow)
if (!cpp20_and_in_constexpr()) {
overflow = __builtin_add_overflow(x, y, &z);
return z;
}
#endif
#endif
// generic, this still optimizes correctly on MSVC.
z = x + y;
overflow = z < x;
return z;
}
// multiply two small integers, getting both the high and low bits.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
limb scalar_mul(limb x, limb y, limb& carry) noexcept {
#ifdef FASTFLOAT_64BIT_LIMB
#if defined(__SIZEOF_INT128__)
// GCC and clang both define it as an extension.
__uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#else
// fallback, no native 128-bit integer multiplication with carry.
// on msvc, this optimizes identically, somehow.
value128 z = full_multiplication(x, y);
bool overflow;
z.low = scalar_add(z.low, carry, overflow);
z.high += uint64_t(overflow); // cannot overflow
carry = z.high;
return z.low;
#endif
#else
uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#endif
}
// add scalar value to bigint starting from offset.
// used in grade school multiplication
template <uint16_t size>
inline FASTFLOAT_CONSTEXPR20
bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
size_t index = start;
limb carry = y;
bool overflow;
while (carry != 0 && index < vec.len()) {
vec[index] = scalar_add(vec[index], carry, overflow);
carry = limb(overflow);
index += 1;
}
if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add scalar value to bigint.
template <uint16_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool small_add(stackvec<size>& vec, limb y) noexcept {
return small_add_from(vec, y, 0);
}
// multiply bigint by scalar value.
template <uint16_t size>
inline FASTFLOAT_CONSTEXPR20
bool small_mul(stackvec<size>& vec, limb y) noexcept {
limb carry = 0;
for (size_t index = 0; index < vec.len(); index++) {
vec[index] = scalar_mul(vec[index], y, carry);
}
if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add bigint to bigint starting from index.
// used in grade school multiplication
template <uint16_t size>
FASTFLOAT_CONSTEXPR20
bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
// the effective x buffer is from `xstart..x.len()`, so exit early
// if we can't get that current range.
if (x.len() < start || y.len() > x.len() - start) {
FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
}
bool carry = false;
for (size_t index = 0; index < y.len(); index++) {
limb xi = x[index + start];
limb yi = y[index];
bool c1 = false;
bool c2 = false;
xi = scalar_add(xi, yi, c1);
if (carry) {
xi = scalar_add(xi, 1, c2);
}
x[index + start] = xi;
carry = c1 | c2;
}
// handle overflow
if (carry) {
FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
}
return true;
}
// add bigint to bigint.
template <uint16_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
return large_add_from(x, y, 0);
}
// grade-school multiplication algorithm
template <uint16_t size>
FASTFLOAT_CONSTEXPR20
bool long_mul(stackvec<size>& x, limb_span y) noexcept {
limb_span xs = limb_span(x.data, x.len());
stackvec<size> z(xs);
limb_span zs = limb_span(z.data, z.len());
if (y.len() != 0) {
limb y0 = y[0];
FASTFLOAT_TRY(small_mul(x, y0));
for (size_t index = 1; index < y.len(); index++) {
limb yi = y[index];
stackvec<size> zi;
if (yi != 0) {
// re-use the same buffer throughout
zi.set_len(0);
FASTFLOAT_TRY(zi.try_extend(zs));
FASTFLOAT_TRY(small_mul(zi, yi));
limb_span zis = limb_span(zi.data, zi.len());
FASTFLOAT_TRY(large_add_from(x, zis, index));
}
}
}
x.normalize();
return true;
}
// grade-school multiplication algorithm
template <uint16_t size>
FASTFLOAT_CONSTEXPR20
bool large_mul(stackvec<size>& x, limb_span y) noexcept {
if (y.len() == 1) {
FASTFLOAT_TRY(small_mul(x, y[0]));
} else {
FASTFLOAT_TRY(long_mul(x, y));
}
return true;
}
template <typename = void>
struct pow5_tables {
static constexpr uint32_t large_step = 135;
static constexpr uint64_t small_power_of_5[] = {
1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
};
#ifdef FASTFLOAT_64BIT_LIMB
constexpr static limb large_power_of_5[] = {
1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
10482974169319127550UL, 198276706040285095UL};
#else
constexpr static limb large_power_of_5[] = {
4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
#endif
};
template <typename T>
constexpr uint32_t pow5_tables<T>::large_step;
template <typename T>
constexpr uint64_t pow5_tables<T>::small_power_of_5[];
template <typename T>
constexpr limb pow5_tables<T>::large_power_of_5[];
// big integer type. implements a small subset of big integer
// arithmetic, using simple algorithms since asymptotically
// faster algorithms are slower for a small number of limbs.
// all operations assume the big-integer is normalized.
struct bigint : pow5_tables<> {
// storage of the limbs, in little-endian order.
stackvec<bigint_limbs> vec;
FASTFLOAT_CONSTEXPR20 bigint(): vec() {}
bigint(const bigint &) = delete;
bigint &operator=(const bigint &) = delete;
bigint(bigint &&) = delete;
bigint &operator=(bigint &&other) = delete;
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value): vec() {
#ifdef FASTFLOAT_64BIT_LIMB
vec.push_unchecked(value);
#else
vec.push_unchecked(uint32_t(value));
vec.push_unchecked(uint32_t(value >> 32));
#endif
vec.normalize();
}
// get the high 64 bits from the vector, and if bits were truncated.
// this is to get the significant digits for the float.
FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool& truncated) const noexcept {
#ifdef FASTFLOAT_64BIT_LIMB
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint64_hi64(vec.rindex(0), truncated);
} else {
uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
truncated |= vec.nonzero(2);
return result;
}
#else
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint32_hi64(vec.rindex(0), truncated);
} else if (vec.len() == 2) {
return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
} else {
uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
truncated |= vec.nonzero(3);
return result;
}
#endif
}
// compare two big integers, returning the large value.
// assumes both are normalized. if the return value is
// negative, other is larger, if the return value is
// positive, this is larger, otherwise they are equal.
// the limbs are stored in little-endian order, so we
// must compare the limbs in ever order.
FASTFLOAT_CONSTEXPR20 int compare(const bigint& other) const noexcept {
if (vec.len() > other.vec.len()) {
return 1;
} else if (vec.len() < other.vec.len()) {
return -1;
} else {
for (size_t index = vec.len(); index > 0; index--) {
limb xi = vec[index - 1];
limb yi = other.vec[index - 1];
if (xi > yi) {
return 1;
} else if (xi < yi) {
return -1;
}
}
return 0;
}
}
// shift left each limb n bits, carrying over to the new limb
// returns true if we were able to shift all the digits.
FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept {
// Internally, for each item, we shift left by n, and add the previous
// right shifted limb-bits.
// For example, we transform (for u8) shifted left 2, to:
// b10100100 b01000010
// b10 b10010001 b00001000
FASTFLOAT_DEBUG_ASSERT(n != 0);
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
size_t shl = n;
size_t shr = limb_bits - shl;
limb prev = 0;
for (size_t index = 0; index < vec.len(); index++) {
limb xi = vec[index];
vec[index] = (xi << shl) | (prev >> shr);
prev = xi;
}
limb carry = prev >> shr;
if (carry != 0) {
return vec.try_push(carry);
}
return true;
}
// move the limbs left by `n` limbs.
FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
FASTFLOAT_DEBUG_ASSERT(n != 0);
if (n + vec.len() > vec.capacity()) {
return false;
} else if (!vec.is_empty()) {
// move limbs
limb* dst = vec.data + n;
const limb* src = vec.data;
std::copy_backward(src, src + vec.len(), dst + vec.len());
// fill in empty limbs
limb* first = vec.data;
limb* last = first + n;
::std::fill(first, last, 0);
vec.set_len(n + vec.len());
return true;
} else {
return true;
}
}
// move the limbs left by `n` bits.
FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
size_t rem = n % limb_bits;
size_t div = n / limb_bits;
if (rem != 0) {
FASTFLOAT_TRY(shl_bits(rem));
}
if (div != 0) {
FASTFLOAT_TRY(shl_limbs(div));
}
return true;
}
// get the number of leading zeros in the bigint.
FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
if (vec.is_empty()) {
return 0;
} else {
#ifdef FASTFLOAT_64BIT_LIMB
return leading_zeroes(vec.rindex(0));
#else
// no use defining a specialized leading_zeroes for a 32-bit type.
uint64_t r0 = vec.rindex(0);
return leading_zeroes(r0 << 32);
#endif
}
}
// get the number of bits in the bigint.
FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
int lz = ctlz();
return int(limb_bits * vec.len()) - lz;
}
FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept {
return small_mul(vec, y);
}
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept {
return small_add(vec, y);
}
// multiply as if by 2 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept {
return shl(exp);
}
// multiply as if by 5 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
// multiply by a power of 5
size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
limb_span large = limb_span(large_power_of_5, large_length);
while (exp >= large_step) {
FASTFLOAT_TRY(large_mul(vec, large));
exp -= large_step;
}
#ifdef FASTFLOAT_64BIT_LIMB
uint32_t small_step = 27;
limb max_native = 7450580596923828125UL;
#else
uint32_t small_step = 13;
limb max_native = 1220703125U;
#endif
while (exp >= small_step) {
FASTFLOAT_TRY(small_mul(vec, max_native));
exp -= small_step;
}
if (exp != 0) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
// This is similar to https://github.com/llvm/llvm-project/issues/47746,
// except the workaround described there don't work here
FASTFLOAT_TRY(
small_mul(vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))
);
}
return true;
}
// multiply as if by 10 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
FASTFLOAT_TRY(pow5(exp));
return pow2(exp);
}
};
} // namespace fast_float
#endif

View File

@ -0,0 +1,40 @@
#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
#define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
#ifdef __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
// Testing for https://wg21.link/N3652, adopted in C++14
#if __cpp_constexpr >= 201304
#define FASTFLOAT_CONSTEXPR14 constexpr
#else
#define FASTFLOAT_CONSTEXPR14
#endif
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
#define FASTFLOAT_HAS_BIT_CAST 1
#else
#define FASTFLOAT_HAS_BIT_CAST 0
#endif
#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
#else
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
#endif
// Testing for relevant C++20 constexpr library features
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED \
&& FASTFLOAT_HAS_BIT_CAST \
&& __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
#define FASTFLOAT_CONSTEXPR20 constexpr
#define FASTFLOAT_IS_CONSTEXPR 1
#else
#define FASTFLOAT_CONSTEXPR20
#define FASTFLOAT_IS_CONSTEXPR 0
#endif
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H

View File

@ -0,0 +1,189 @@
#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H
#define FASTFLOAT_DECIMAL_TO_BINARY_H
#include "float_common.h"
#include "fast_table.h"
#include <cfloat>
#include <cinttypes>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <cstring>
namespace fast_float {
// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating
// the result, with the "high" part corresponding to the most significant bits and the
// low part corresponding to the least significant bits.
//
template <int bit_precision>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
value128 compute_product_approximation(int64_t q, uint64_t w) {
const int index = 2 * int(q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact because
// The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
// gives the exact answer.
value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]);
static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]");
constexpr uint64_t precision_mask = (bit_precision < 64) ?
(uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
: uint64_t(0xFFFFFFFFFFFFFFFF);
if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower)
// regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed.
value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) {
firstproduct.high++;
}
}
return firstproduct;
}
namespace detail {
/**
* For q in (0,350), we have that
* f = (((152170 + 65536) * q ) >> 16);
* is equal to
* floor(p) + q
* where
* p = log(5**q)/log(2) = q * log(5)/log(2)
*
* For negative values of q in (-400,0), we have that
* f = (((152170 + 65536) * q ) >> 16);
* is equal to
* -ceil(p) + q
* where
* p = log(5**-q)/log(2) = -q * log(5)/log(2)
*/
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
return (((152170 + 65536) * q) >> 16) + 63;
}
} // namespace detail
// create an adjusted mantissa, biased by the invalid power2
// for significant digits already multiplied by 10 ** q.
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
int hilz = int(w >> 63) ^ 1;
adjusted_mantissa answer;
answer.mantissa = w << hilz;
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias);
return answer;
}
// w * 10 ** q, without rounding the representation up.
// the power2 in the exponent will be adjusted by invalid_am_bias.
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
int lz = leading_zeroes(w);
w <<= lz;
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
return compute_error_scaled<binary>(q, product.high, lz);
}
// w * 10 ** q
// The returned value should be a valid ieee64 number that simply need to be packed.
// However, in some very rare cases, the computation will fail. In such cases, we
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
// in such cases.
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
adjusted_mantissa answer;
if ((w == 0) || (q < binary::smallest_power_of_ten())) {
answer.power2 = 0;
answer.mantissa = 0;
// result should be zero
return answer;
}
if (q > binary::largest_power_of_ten()) {
// we want to get infinity:
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
// At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five].
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(w);
w <<= lz;
// The required precision is binary::mantissa_explicit_bits() + 3 because
// 1. We need the implicit bit
// 2. We need an extra bit for rounding purposes
// 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
// The computed 'product' is always sufficient.
// Mathematical proof:
// Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear)
// See script/mushtak_lemire.py
// The "compute_product_approximation" function can be slightly slower than a branchless approach:
// value128 product = compute_product(q, w);
// but in practice, we can win big with the compute_product_approximation if its additional branch
// is easily predicted. Which is best is data specific.
int upperbit = int(product.high >> 63);
answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent());
if (answer.power2 <= 0) { // we have a subnormal?
// Here have that answer.power2 <= 0 so -answer.power2 >= 0
if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
answer.power2 = 0;
answer.mantissa = 0;
// result should be zero
return answer;
}
// next line is safe because -answer.power2 + 1 < 64
answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1;
return answer;
}
// usually, we round *up*, but if we fall right in between and and we have an
// even basis, we need to round down
// We are only concerned with the cases where 5**q fits in single 64-bit word.
if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
((answer.mantissa & 3) == 1) ) { // we may fall between two floats!
// To be in-between two floats we need that in doing
// answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
// ... we dropped out only zeroes. But if this happened, then we can go back!!!
if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) {
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
answer.power2++; // undo previous addition
}
answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
if (answer.power2 >= binary::infinite_power()) { // infinity
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
}
return answer;
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,426 @@
#ifndef FASTFLOAT_DIGIT_COMPARISON_H
#define FASTFLOAT_DIGIT_COMPARISON_H
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator>
#include "float_common.h"
#include "bigint.h"
#include "ascii_number.h"
namespace fast_float {
// 1e0 to 1e19
constexpr static uint64_t powers_of_ten_uint64[] = {
1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL,
1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL,
100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL,
1000000000000000000UL, 10000000000000000000UL};
// calculate the exponent, in scientific notation, of the number.
// this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
int32_t scientific_exponent(parsed_number_string_t<UC> & num) noexcept {
uint64_t mantissa = num.mantissa;
int32_t exponent = int32_t(num.exponent);
while (mantissa >= 10000) {
mantissa /= 10000;
exponent += 4;
}
while (mantissa >= 100) {
mantissa /= 100;
exponent += 2;
}
while (mantissa >= 10) {
mantissa /= 10;
exponent += 1;
}
return exponent;
}
// this converts a native floating-point number to an extended-precision float.
template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa to_extended(T value) noexcept {
using equiv_uint = typename binary_format<T>::equiv_uint;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am;
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
equiv_uint bits;
#if FASTFLOAT_HAS_BIT_CAST
bits = std::bit_cast<equiv_uint>(value);
#else
::memcpy(&bits, &value, sizeof(T));
#endif
if ((bits & exponent_mask) == 0) {
// denormal
am.power2 = 1 - bias;
am.mantissa = bits & mantissa_mask;
} else {
// normal
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
}
return am;
}
// get the extended precision value of the halfway point between b and b+u.
// we are given a native float that represents b, so we need to adjust it
// halfway between b and b+u.
template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa to_extended_halfway(T value) noexcept {
adjusted_mantissa am = to_extended(value);
am.mantissa <<= 1;
am.mantissa += 1;
am.power2 -= 1;
return am;
}
// round an extended-precision float to the nearest machine float.
template <typename T, typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round(adjusted_mantissa& am, callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) {
// have a denormal float
int32_t shift = -am.power2 + 1;
cb(am, std::min<int32_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
return;
}
// have a normal float, use the default shift.
cb(am, mantissa_shift);
// check for carry
if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
am.power2++;
}
// check for infinite: we could have carried to an infinite power
am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
if (am.power2 >= binary_format<T>::infinite_power()) {
am.power2 = binary_format<T>::infinite_power();
am.mantissa = 0;
}
}
template <typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
const uint64_t mask
= (shift == 64)
? UINT64_MAX
: (uint64_t(1) << shift) - 1;
const uint64_t halfway
= (shift == 0)
? 0
: uint64_t(1) << (shift - 1);
uint64_t truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway;
// shift digits into position
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
bool is_odd = (am.mantissa & 1) == 1;
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void skip_zeros(UC const * & first, UC const * last) noexcept {
uint64_t val;
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
break;
}
first += int_cmp_len<UC>();
}
while (first != last) {
if (*first != UC('0')) {
break;
}
first++;
}
}
// determine if any non-zero digits were truncated.
// all characters must be valid digits.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool is_truncated(UC const * first, UC const * last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val;
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
return true;
}
first += int_cmp_len<UC>();
}
while (first != last) {
if (*first != UC('0')) {
return true;
}
++first;
}
return false;
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
bool is_truncated(span<const UC> s) noexcept {
return is_truncated(s.ptr, s.ptr + s.len());
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void parse_eight_digits(const UC*& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
counter += 8;
count += 8;
}
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void parse_one_digit(UC const *& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 10 + limb(*p - UC('0'));
p++;
counter++;
count++;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void add_native(bigint& big, limb power, limb value) noexcept {
big.mul(power);
big.add(value);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void round_up_bigint(bigint& big, size_t& count) noexcept {
// need to round-up the digits, but need to avoid rounding
// ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1);
count++;
}
// parse the significant digits into a big integer
template <typename UC>
inline FASTFLOAT_CONSTEXPR20
void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_digits, size_t& digits) noexcept {
// try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step.
size_t counter = 0;
digits = 0;
limb value = 0;
#ifdef FASTFLOAT_64BIT_LIMB
size_t step = 19;
#else
size_t step = 9;
#endif
// process all integer digits.
UC const * p = num.integer.ptr;
UC const * pend = p + num.integer.len();
skip_zeros(p, pend);
// process all digits, in increments of step per loop
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (num.fraction.ptr != nullptr) {
truncated |= is_truncated(num.fraction);
}
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
// add our fraction digits, if they're available.
if (num.fraction.ptr != nullptr) {
p = num.fraction.ptr;
pend = p + num.fraction.len();
if (digits == 0) {
skip_zeros(p, pend);
}
// process all digits, in increments of step per loop
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
}
if (counter != 0) {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
}
}
template <typename T>
inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
adjusted_mantissa answer;
bool truncated;
answer.mantissa = bigmant.hi64(truncated);
int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
answer.power2 = bigmant.bit_length() - 64 + bias;
round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) {
round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
return is_above || (is_halfway && truncated) || (is_odd && is_halfway);
});
});
return answer;
}
// the scaling here is quite simple: we have, for the real digits `m * 10^e`,
// and for the theoretical digits `n * 2^f`. Since `e` is always negative,
// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`.
// we then need to scale by `2^(f- e)`, and then the two significant digits
// are of the same magnitude.
template <typename T>
inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
bigint& real_digits = bigmant;
int32_t real_exp = exponent;
// get the value of `b`, rounded down, and get a bigint representation of b+h
adjusted_mantissa am_b = am;
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type.
round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); });
T b;
to_float(false, am_b, b);
adjusted_mantissa theor = to_extended_halfway(b);
bigint theor_digits(theor.mantissa);
int32_t theor_exp = theor.power2;
// scale real digits and theor digits to be same power.
int32_t pow2_exp = theor_exp - real_exp;
uint32_t pow5_exp = uint32_t(-real_exp);
if (pow5_exp != 0) {
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
}
if (pow2_exp > 0) {
FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
} else if (pow2_exp < 0) {
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
}
// compare digits, and use it to director rounding
int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am;
round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) {
round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
(void)_; // not needed, since we've done our comparison
(void)__; // not needed, since we've done our comparison
if (ord > 0) {
return true;
} else if (ord < 0) {
return false;
} else {
return is_odd;
}
});
});
return answer;
}
// parse the significant digits as a big integer to unambiguously round the
// the significant digits. here, we are trying to determine how to round
// an extended float representation close to `b+h`, halfway between `b`
// (the float rounded-down) and `b+u`, the next positive float. this
// algorithm is always correct, and uses one of two approaches. when
// the exponent is positive relative to the significant digits (such as
// 1234), we create a big-integer representation, get the high 64-bits,
// determine if any lower bits are truncated, and use that to direct
// rounding. in case of a negative exponent relative to the significant
// digits (such as 1.2345), we create a theoretical representation of
// `b` as a big-integer type, scaled to the same binary exponent as
// the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding.
template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20
adjusted_mantissa digit_comp(parsed_number_string_t<UC>& num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias
am.power2 -= invalid_am_bias;
int32_t sci_exp = scientific_exponent(num);
size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0;
bigint bigmant;
parse_mantissa(bigmant, num, max_digits, digits);
// can't underflow, since digits is at most max_digits.
int32_t exponent = sci_exp + 1 - int32_t(digits);
if (exponent >= 0) {
return positive_digit_comp<T>(bigmant, exponent);
} else {
return negative_digit_comp<T>(bigmant, am, exponent);
}
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,42 @@
#ifndef FASTFLOAT_FAST_FLOAT_H
#define FASTFLOAT_FAST_FLOAT_H
#include "float_common.h"
namespace fast_float {
/**
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
* a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale.
* The resulting floating-point value is the closest floating-point values (using either float or double),
* using the "round to even" convention for values that would otherwise fall right in-between two values.
* That is, we provide exact parsing according to the IEEE standard.
*
* Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
* parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
* `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
*
* The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
*
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
* the type `fast_float::chars_format`. It is a bitset value: we check whether
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
* to determine whether we allow the fixed point and scientific notation respectively.
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
*/
template<typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
T &value, chars_format fmt = chars_format::general) noexcept;
/**
* Like from_chars, but accepts an `options` argument to govern number parsing.
*/
template<typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
T &value, parse_options_t<UC> options) noexcept;
} // namespace fast_float
#include "parse_number.h"
#endif // FASTFLOAT_FAST_FLOAT_H

View File

@ -0,0 +1,700 @@
#ifndef FASTFLOAT_FAST_TABLE_H
#define FASTFLOAT_FAST_TABLE_H
#include <cstdint>
namespace fast_float {
/**
* When mapping numbers from decimal to binary,
* we go from w * 10^q to m * 2^p but we have
* 10^q = 5^q * 2^q, so effectively
* we are trying to match
* w * 2^q * 5^q to m * 2^p. Thus the powers of two
* are not a concern since they can be represented
* exactly using the binary notation, only the powers of five
* affect the binary significand.
*/
/**
* The smallest non-zero float (binary64) is 2^-1074.
* We take as input numbers of the form w x 10^q where w < 2^64.
* We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076.
* However, we have that
* (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074.
* Thus it is possible for a number of the form w * 10^-342 where
* w is a 64-bit value to be a non-zero floating-point number.
*********
* Any number of form w * 10^309 where w>= 1 is going to be
* infinite in binary64 so we never need to worry about powers
* of 5 greater than 308.
*/
template <class unused = void>
struct powers_template {
constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten();
constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten();
constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1);
// Powers of five from 5^-342 all the way to 5^308 rounded toward one.
constexpr static uint64_t power_of_five_128[number_of_entries] = {
0xeef453d6923bd65a,0x113faa2906a13b3f,
0x9558b4661b6565f8,0x4ac7ca59a424c507,
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
0xe95a99df8ace6f53,0xf4d82c2c107973dc,
0x91d8a02bb6c10594,0x79071b9b8a4be869,
0xb64ec836a47146f9,0x9748e2826cdee284,
0xe3e27a444d8d98b7,0xfd1b1b2308169b25,
0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7,
0xb208ef855c969f4f,0xbdbd2d335e51a935,
0xde8b2b66b3bc4723,0xad2c788035e61382,
0x8b16fb203055ac76,0x4c3bcb5021afcc31,
0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d,
0xd953e8624b85dd78,0xd71d6dad34a2af0d,
0x87d4713d6f33aa6b,0x8672648c40e5ad68,
0xa9c98d8ccb009506,0x680efdaf511f18c2,
0xd43bf0effdc0ba48,0x212bd1b2566def2,
0x84a57695fe98746d,0x14bb630f7604b57,
0xa5ced43b7e3e9188,0x419ea3bd35385e2d,
0xcf42894a5dce35ea,0x52064cac828675b9,
0x818995ce7aa0e1b2,0x7343efebd1940993,
0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8,
0xca66fa129f9b60a6,0xd41a26e077774ef6,
0xfd00b897478238d0,0x8920b098955522b4,
0x9e20735e8cb16382,0x55b46e5f5d5535b0,
0xc5a890362fddbc62,0xeb2189f734aa831d,
0xf712b443bbd52b7b,0xa5e9ec7501d523e4,
0x9a6bb0aa55653b2d,0x47b233c92125366e,
0xc1069cd4eabe89f8,0x999ec0bb696e840a,
0xf148440a256e2c76,0xc00670ea43ca250d,
0x96cd2a865764dbca,0x380406926a5e5728,
0xbc807527ed3e12bc,0xc605083704f5ecf2,
0xeba09271e88d976b,0xf7864a44c633682e,
0x93445b8731587ea3,0x7ab3ee6afbe0211d,
0xb8157268fdae9e4c,0x5960ea05bad82964,
0xe61acf033d1a45df,0x6fb92487298e33bd,
0x8fd0c16206306bab,0xa5d3b6d479f8e056,
0xb3c4f1ba87bc8696,0x8f48a4899877186c,
0xe0b62e2929aba83c,0x331acdabfe94de87,
0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14,
0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9,
0xdb71e91432b1a24a,0xc9e82cd9f69d6150,
0x892731ac9faf056e,0xbe311c083a225cd2,
0xab70fe17c79ac6ca,0x6dbd630a48aaf406,
0xd64d3d9db981787d,0x92cbbccdad5b108,
0x85f0468293f0eb4e,0x25bbf56008c58ea5,
0xa76c582338ed2621,0xaf2af2b80af6f24e,
0xd1476e2c07286faa,0x1af5af660db4aee1,
0x82cca4db847945ca,0x50d98d9fc890ed4d,
0xa37fce126597973c,0xe50ff107bab528a0,
0xcc5fc196fefd7d0c,0x1e53ed49a96272c8,
0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a,
0x9faacf3df73609b1,0x77b191618c54e9ac,
0xc795830d75038c1d,0xd59df5b9ef6a2417,
0xf97ae3d0d2446f25,0x4b0573286b44ad1d,
0x9becce62836ac577,0x4ee367f9430aec32,
0xc2e801fb244576d5,0x229c41f793cda73f,
0xf3a20279ed56d48a,0x6b43527578c1110f,
0x9845418c345644d6,0x830a13896b78aaa9,
0xbe5691ef416bd60c,0x23cc986bc656d553,
0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8,
0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9,
0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53,
0xe858ad248f5c22c9,0xd1b3400f8f9cff68,
0x91376c36d99995be,0x23100809b9c21fa1,
0xb58547448ffffb2d,0xabd40a0c2832a78a,
0xe2e69915b3fff9f9,0x16c90c8f323f516c,
0x8dd01fad907ffc3b,0xae3da7d97f6792e3,
0xb1442798f49ffb4a,0x99cd11cfdf41779c,
0xdd95317f31c7fa1d,0x40405643d711d583,
0x8a7d3eef7f1cfc52,0x482835ea666b2572,
0xad1c8eab5ee43b66,0xda3243650005eecf,
0xd863b256369d4a40,0x90bed43e40076a82,
0x873e4f75e2224e68,0x5a7744a6e804a291,
0xa90de3535aaae202,0x711515d0a205cb36,
0xd3515c2831559a83,0xd5a5b44ca873e03,
0x8412d9991ed58091,0xe858790afe9486c2,
0xa5178fff668ae0b6,0x626e974dbe39a872,
0xce5d73ff402d98e3,0xfb0a3d212dc8128f,
0x80fa687f881c7f8e,0x7ce66634bc9d0b99,
0xa139029f6a239f72,0x1c1fffc1ebc44e80,
0xc987434744ac874e,0xa327ffb266b56220,
0xfbe9141915d7a922,0x4bf1ff9f0062baa8,
0x9d71ac8fada6c9b5,0x6f773fc3603db4a9,
0xc4ce17b399107c22,0xcb550fb4384d21d3,
0xf6019da07f549b2b,0x7e2a53a146606a48,
0x99c102844f94e0fb,0x2eda7444cbfc426d,
0xc0314325637a1939,0xfa911155fefb5308,
0xf03d93eebc589f88,0x793555ab7eba27ca,
0x96267c7535b763b5,0x4bc1558b2f3458de,
0xbbb01b9283253ca2,0x9eb1aaedfb016f16,
0xea9c227723ee8bcb,0x465e15a979c1cadc,
0x92a1958a7675175f,0xbfacd89ec191ec9,
0xb749faed14125d36,0xcef980ec671f667b,
0xe51c79a85916f484,0x82b7e12780e7401a,
0x8f31cc0937ae58d2,0xd1b2ecb8b0908810,
0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15,
0xdfbdcece67006ac9,0x67a791e093e1d49a,
0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0,
0xaecc49914078536d,0x58fae9f773886e18,
0xda7f5bf590966848,0xaf39a475506a899e,
0x888f99797a5e012d,0x6d8406c952429603,
0xaab37fd7d8f58178,0xc8e5087ba6d33b83,
0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64,
0x855c3be0a17fcd26,0x5cf2eea09a55067f,
0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e,
0xd0601d8efc57b08b,0xf13b94daf124da26,
0x823c12795db6ce57,0x76c53d08d6b70858,
0xa2cb1717b52481ed,0x54768c4b0c64ca6e,
0xcb7ddcdda26da268,0xa9942f5dcf7dfd09,
0xfe5d54150b090b02,0xd3f93b35435d7c4c,
0x9efa548d26e5a6e1,0xc47bc5014a1a6daf,
0xc6b8e9b0709f109a,0x359ab6419ca1091b,
0xf867241c8cc6d4c0,0xc30163d203c94b62,
0x9b407691d7fc44f8,0x79e0de63425dcf1d,
0xc21094364dfb5636,0x985915fc12f542e4,
0xf294b943e17a2bc4,0x3e6f5b7b17b2939d,
0x979cf3ca6cec5b5a,0xa705992ceecf9c42,
0xbd8430bd08277231,0x50c6ff782a838353,
0xece53cec4a314ebd,0xa4f8bf5635246428,
0x940f4613ae5ed136,0x871b7795e136be99,
0xb913179899f68584,0x28e2557b59846e3f,
0xe757dd7ec07426e5,0x331aeada2fe589cf,
0x9096ea6f3848984f,0x3ff0d2c85def7621,
0xb4bca50b065abe63,0xfed077a756b53a9,
0xe1ebce4dc7f16dfb,0xd3e8495912c62894,
0x8d3360f09cf6e4bd,0x64712dd7abbbd95c,
0xb080392cc4349dec,0xbd8d794d96aacfb3,
0xdca04777f541c567,0xecf0d7a0fc5583a0,
0x89e42caaf9491b60,0xf41686c49db57244,
0xac5d37d5b79b6239,0x311c2875c522ced5,
0xd77485cb25823ac7,0x7d633293366b828b,
0x86a8d39ef77164bc,0xae5dff9c02033197,
0xa8530886b54dbdeb,0xd9f57f830283fdfc,
0xd267caa862a12d66,0xd072df63c324fd7b,
0x8380dea93da4bc60,0x4247cb9e59f71e6d,
0xa46116538d0deb78,0x52d9be85f074e608,
0xcd795be870516656,0x67902e276c921f8b,
0x806bd9714632dff6,0xba1cd8a3db53b6,
0xa086cfcd97bf97f3,0x80e8a40eccd228a4,
0xc8a883c0fdaf7df0,0x6122cd128006b2cd,
0xfad2a4b13d1b5d6c,0x796b805720085f81,
0x9cc3a6eec6311a63,0xcbe3303674053bb0,
0xc3f490aa77bd60fc,0xbedbfc4411068a9c,
0xf4f1b4d515acb93b,0xee92fb5515482d44,
0x991711052d8bf3c5,0x751bdd152d4d1c4a,
0xbf5cd54678eef0b6,0xd262d45a78a0635d,
0xef340a98172aace4,0x86fb897116c87c34,
0x9580869f0e7aac0e,0xd45d35e6ae3d4da0,
0xbae0a846d2195712,0x8974836059cca109,
0xe998d258869facd7,0x2bd1a438703fc94b,
0x91ff83775423cc06,0x7b6306a34627ddcf,
0xb67f6455292cbf08,0x1a3bc84c17b1d542,
0xe41f3d6a7377eeca,0x20caba5f1d9e4a93,
0x8e938662882af53e,0x547eb47b7282ee9c,
0xb23867fb2a35b28d,0xe99e619a4f23aa43,
0xdec681f9f4c31f31,0x6405fa00e2ec94d4,
0x8b3c113c38f9f37e,0xde83bc408dd3dd04,
0xae0b158b4738705e,0x9624ab50b148d445,
0xd98ddaee19068c76,0x3badd624dd9b0957,
0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6,
0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c,
0xd47487cc8470652b,0x7647c3200069671f,
0x84c8d4dfd2c63f3b,0x29ecd9f40041e073,
0xa5fb0a17c777cf09,0xf468107100525890,
0xcf79cc9db955c2cc,0x7182148d4066eeb4,
0x81ac1fe293d599bf,0xc6f14cd848405530,
0xa21727db38cb002f,0xb8ada00e5a506a7c,
0xca9cf1d206fdc03b,0xa6d90811f0e4851c,
0xfd442e4688bd304a,0x908f4a166d1da663,
0x9e4a9cec15763e2e,0x9a598e4e043287fe,
0xc5dd44271ad3cdba,0x40eff1e1853f29fd,
0xf7549530e188c128,0xd12bee59e68ef47c,
0x9a94dd3e8cf578b9,0x82bb74f8301958ce,
0xc13a148e3032d6e7,0xe36a52363c1faf01,
0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1,
0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9,
0xbcb2b812db11a5de,0x7415d448f6b6f0e7,
0xebdf661791d60f56,0x111b495b3464ad21,
0x936b9fcebb25c995,0xcab10dd900beec34,
0xb84687c269ef3bfb,0x3d5d514f40eea742,
0xe65829b3046b0afa,0xcb4a5a3112a5112,
0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab,
0xb3f4e093db73a093,0x59ed216765690f56,
0xe0f218b8d25088b8,0x306869c13ec3532c,
0x8c974f7383725573,0x1e414218c73a13fb,
0xafbd2350644eeacf,0xe5d1929ef90898fa,
0xdbac6c247d62a583,0xdf45f746b74abf39,
0x894bc396ce5da772,0x6b8bba8c328eb783,
0xab9eb47c81f5114f,0x66ea92f3f326564,
0xd686619ba27255a2,0xc80a537b0efefebd,
0x8613fd0145877585,0xbd06742ce95f5f36,
0xa798fc4196e952e7,0x2c48113823b73704,
0xd17f3b51fca3a7a0,0xf75a15862ca504c5,
0x82ef85133de648c4,0x9a984d73dbe722fb,
0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba,
0xcc963fee10b7d1b3,0x318df905079926a8,
0xffbbcfe994e5c61f,0xfdf17746497f7052,
0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633,
0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0,
0xf9bd690a1b68637b,0x3dfdce7aa3c673b0,
0x9c1661a651213e2d,0x6bea10ca65c084e,
0xc31bfa0fe5698db8,0x486e494fcff30a62,
0xf3e2f893dec3f126,0x5a89dba3c3efccfa,
0x986ddb5c6b3a76b7,0xf89629465a75e01c,
0xbe89523386091465,0xf6bbb397f1135823,
0xee2ba6c0678b597f,0x746aa07ded582e2c,
0x94db483840b717ef,0xa8c2a44eb4571cdc,
0xba121a4650e4ddeb,0x92f34d62616ce413,
0xe896a0d7e51e1566,0x77b020baf9c81d17,
0x915e2486ef32cd60,0xace1474dc1d122e,
0xb5b5ada8aaff80b8,0xd819992132456ba,
0xe3231912d5bf60e6,0x10e1fff697ed6c69,
0x8df5efabc5979c8f,0xca8d3ffa1ef463c1,
0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2,
0xddd0467c64bce4a0,0xac7cb3f6d05ddbde,
0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b,
0xad4ab7112eb3929d,0x86c16c98d2c953c6,
0xd89d64d57a607744,0xe871c7bf077ba8b7,
0x87625f056c7c4a8b,0x11471cd764ad4972,
0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf,
0xd389b47879823479,0x4aff1d108d4ec2c3,
0x843610cb4bf160cb,0xcedf722a585139ba,
0xa54394fe1eedb8fe,0xc2974eb4ee658828,
0xce947a3da6a9273e,0x733d226229feea32,
0x811ccc668829b887,0x806357d5a3f525f,
0xa163ff802a3426a8,0xca07c2dcb0cf26f7,
0xc9bcff6034c13052,0xfc89b393dd02f0b5,
0xfc2c3f3841f17c67,0xbbac2078d443ace2,
0x9d9ba7832936edc0,0xd54b944b84aa4c0d,
0xc5029163f384a931,0xa9e795e65d4df11,
0xf64335bcf065d37d,0x4d4617b5ff4a16d5,
0x99ea0196163fa42e,0x504bced1bf8e4e45,
0xc06481fb9bcf8d39,0xe45ec2862f71e1d6,
0xf07da27a82c37088,0x5d767327bb4e5a4c,
0x964e858c91ba2655,0x3a6a07f8d510f86f,
0xbbe226efb628afea,0x890489f70a55368b,
0xeadab0aba3b2dbe5,0x2b45ac74ccea842e,
0x92c8ae6b464fc96f,0x3b0b8bc90012929d,
0xb77ada0617e3bbcb,0x9ce6ebb40173744,
0xe55990879ddcaabd,0xcc420a6a101d0515,
0x8f57fa54c2a9eab6,0x9fa946824a12232d,
0xb32df8e9f3546564,0x47939822dc96abf9,
0xdff9772470297ebd,0x59787e2b93bc56f7,
0x8bfbea76c619ef36,0x57eb4edb3c55b65a,
0xaefae51477a06b03,0xede622920b6b23f1,
0xdab99e59958885c4,0xe95fab368e45eced,
0x88b402f7fd75539b,0x11dbcb0218ebb414,
0xaae103b5fcd2a881,0xd652bdc29f26a119,
0xd59944a37c0752a2,0x4be76d3346f0495f,
0x857fcae62d8493a5,0x6f70a4400c562ddb,
0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952,
0xd097ad07a71f26b2,0x7e2000a41346a7a7,
0x825ecc24c873782f,0x8ed400668c0c28c8,
0xa2f67f2dfa90563b,0x728900802f0f32fa,
0xcbb41ef979346bca,0x4f2b40a03ad2ffb9,
0xfea126b7d78186bc,0xe2f610c84987bfa8,
0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9,
0xc6ede63fa05d3143,0x91503d1c79720dbb,
0xf8a95fcf88747d94,0x75a44c6397ce912a,
0x9b69dbe1b548ce7c,0xc986afbe3ee11aba,
0xc24452da229b021b,0xfbe85badce996168,
0xf2d56790ab41c2a2,0xfae27299423fb9c3,
0x97c560ba6b0919a5,0xdccd879fc967d41a,
0xbdb6b8e905cb600f,0x5400e987bbc1c920,
0xed246723473e3813,0x290123e9aab23b68,
0x9436c0760c86e30b,0xf9a0b6720aaf6521,
0xb94470938fa89bce,0xf808e40e8d5b3e69,
0xe7958cb87392c2c2,0xb60b1d1230b20e04,
0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2,
0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3,
0xe2280b6c20dd5232,0x25c6da63c38de1b0,
0x8d590723948a535f,0x579c487e5a38ad0e,
0xb0af48ec79ace837,0x2d835a9df0c6d851,
0xdcdb1b2798182244,0xf8e431456cf88e65,
0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff,
0xac8b2d36eed2dac5,0xe272467e3d222f3f,
0xd7adf884aa879177,0x5b0ed81dcc6abb0f,
0x86ccbb52ea94baea,0x98e947129fc2b4e9,
0xa87fea27a539e9a5,0x3f2398d747b36224,
0xd29fe4b18e88640e,0x8eec7f0d19a03aad,
0x83a3eeeef9153e89,0x1953cf68300424ac,
0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7,
0xcdb02555653131b6,0x3792f412cb06794d,
0x808e17555f3ebf11,0xe2bbd88bbee40bd0,
0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4,
0xc8de047564d20a8b,0xf245825a5a445275,
0xfb158592be068d2e,0xeed6e2f0f0d56712,
0x9ced737bb6c4183d,0x55464dd69685606b,
0xc428d05aa4751e4c,0xaa97e14c3c26b886,
0xf53304714d9265df,0xd53dd99f4b3066a8,
0x993fe2c6d07b7fab,0xe546a8038efe4029,
0xbf8fdb78849a5f96,0xde98520472bdd033,
0xef73d256a5c0f77c,0x963e66858f6d4440,
0x95a8637627989aad,0xdde7001379a44aa8,
0xbb127c53b17ec159,0x5560c018580d5d52,
0xe9d71b689dde71af,0xaab8f01e6e10b4a6,
0x9226712162ab070d,0xcab3961304ca70e8,
0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22,
0xe45c10c42a2b3b05,0x8cb89a7db77c506a,
0x8eb98a7a9a5b04e3,0x77f3608e92adb242,
0xb267ed1940f1c61c,0x55f038b237591ed3,
0xdf01e85f912e37a3,0x6b6c46dec52f6688,
0x8b61313bbabce2c6,0x2323ac4b3b3da015,
0xae397d8aa96c1b77,0xabec975e0a0d081a,
0xd9c7dced53c72255,0x96e7bd358c904a21,
0x881cea14545c7575,0x7e50d64177da2e54,
0xaa242499697392d2,0xdde50bd1d5d0b9e9,
0xd4ad2dbfc3d07787,0x955e4ec64b44e864,
0x84ec3c97da624ab4,0xbd5af13bef0b113e,
0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e,
0xcfb11ead453994ba,0x67de18eda5814af2,
0x81ceb32c4b43fcf4,0x80eacf948770ced7,
0xa2425ff75e14fc31,0xa1258379a94d028d,
0xcad2f7f5359a3b3e,0x96ee45813a04330,
0xfd87b5f28300ca0d,0x8bca9d6e188853fc,
0x9e74d1b791e07e48,0x775ea264cf55347e,
0xc612062576589dda,0x95364afe032a819e,
0xf79687aed3eec551,0x3a83ddbd83f52205,
0x9abe14cd44753b52,0xc4926a9672793543,
0xc16d9a0095928a27,0x75b7053c0f178294,
0xf1c90080baf72cb1,0x5324c68b12dd6339,
0x971da05074da7bee,0xd3f6fc16ebca5e04,
0xbce5086492111aea,0x88f4bb1ca6bcf585,
0xec1e4a7db69561a5,0x2b31e9e3d06c32e6,
0x9392ee8e921d5d07,0x3aff322e62439fd0,
0xb877aa3236a4b449,0x9befeb9fad487c3,
0xe69594bec44de15b,0x4c2ebe687989a9b4,
0x901d7cf73ab0acd9,0xf9d37014bf60a11,
0xb424dc35095cd80f,0x538484c19ef38c95,
0xe12e13424bb40e13,0x2865a5f206b06fba,
0x8cbccc096f5088cb,0xf93f87b7442e45d4,
0xafebff0bcb24aafe,0xf78f69a51539d749,
0xdbe6fecebdedd5be,0xb573440e5a884d1c,
0x89705f4136b4a597,0x31680a88f8953031,
0xabcc77118461cefc,0xfdc20d2b36ba7c3e,
0xd6bf94d5e57a42bc,0x3d32907604691b4d,
0x8637bd05af6c69b5,0xa63f9a49c2c1b110,
0xa7c5ac471b478423,0xfcf80dc33721d54,
0xd1b71758e219652b,0xd3c36113404ea4a9,
0x83126e978d4fdf3b,0x645a1cac083126ea,
0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4,
0xcccccccccccccccc,0xcccccccccccccccd,
0x8000000000000000,0x0,
0xa000000000000000,0x0,
0xc800000000000000,0x0,
0xfa00000000000000,0x0,
0x9c40000000000000,0x0,
0xc350000000000000,0x0,
0xf424000000000000,0x0,
0x9896800000000000,0x0,
0xbebc200000000000,0x0,
0xee6b280000000000,0x0,
0x9502f90000000000,0x0,
0xba43b74000000000,0x0,
0xe8d4a51000000000,0x0,
0x9184e72a00000000,0x0,
0xb5e620f480000000,0x0,
0xe35fa931a0000000,0x0,
0x8e1bc9bf04000000,0x0,
0xb1a2bc2ec5000000,0x0,
0xde0b6b3a76400000,0x0,
0x8ac7230489e80000,0x0,
0xad78ebc5ac620000,0x0,
0xd8d726b7177a8000,0x0,
0x878678326eac9000,0x0,
0xa968163f0a57b400,0x0,
0xd3c21bcecceda100,0x0,
0x84595161401484a0,0x0,
0xa56fa5b99019a5c8,0x0,
0xcecb8f27f4200f3a,0x0,
0x813f3978f8940984,0x4000000000000000,
0xa18f07d736b90be5,0x5000000000000000,
0xc9f2c9cd04674ede,0xa400000000000000,
0xfc6f7c4045812296,0x4d00000000000000,
0x9dc5ada82b70b59d,0xf020000000000000,
0xc5371912364ce305,0x6c28000000000000,
0xf684df56c3e01bc6,0xc732000000000000,
0x9a130b963a6c115c,0x3c7f400000000000,
0xc097ce7bc90715b3,0x4b9f100000000000,
0xf0bdc21abb48db20,0x1e86d40000000000,
0x96769950b50d88f4,0x1314448000000000,
0xbc143fa4e250eb31,0x17d955a000000000,
0xeb194f8e1ae525fd,0x5dcfab0800000000,
0x92efd1b8d0cf37be,0x5aa1cae500000000,
0xb7abc627050305ad,0xf14a3d9e40000000,
0xe596b7b0c643c719,0x6d9ccd05d0000000,
0x8f7e32ce7bea5c6f,0xe4820023a2000000,
0xb35dbf821ae4f38b,0xdda2802c8a800000,
0xe0352f62a19e306e,0xd50b2037ad200000,
0x8c213d9da502de45,0x4526f422cc340000,
0xaf298d050e4395d6,0x9670b12b7f410000,
0xdaf3f04651d47b4c,0x3c0cdd765f114000,
0x88d8762bf324cd0f,0xa5880a69fb6ac800,
0xab0e93b6efee0053,0x8eea0d047a457a00,
0xd5d238a4abe98068,0x72a4904598d6d880,
0x85a36366eb71f041,0x47a6da2b7f864750,
0xa70c3c40a64e6c51,0x999090b65f67d924,
0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d,
0x82818f1281ed449f,0xbff8f10e7a8921a4,
0xa321f2d7226895c7,0xaff72d52192b6a0d,
0xcbea6f8ceb02bb39,0x9bf4f8a69f764490,
0xfee50b7025c36a08,0x2f236d04753d5b4,
0x9f4f2726179a2245,0x1d762422c946590,
0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5,
0xf8ebad2b84e0d58b,0xd2e0898765a7deb2,
0x9b934c3b330c8577,0x63cc55f49f88eb2f,
0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb,
0xf316271c7fc3908a,0x8bef464e3945ef7a,
0x97edd871cfda3a56,0x97758bf0e3cbb5ac,
0xbde94e8e43d0c8ec,0x3d52eeed1cbea317,
0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd,
0x945e455f24fb1cf8,0x8fe8caa93e74ef6a,
0xb975d6b6ee39e436,0xb3e2fd538e122b44,
0xe7d34c64a9c85d44,0x60dbbca87196b616,
0x90e40fbeea1d3a4a,0xbc8955e946fe31cd,
0xb51d13aea4a488dd,0x6babab6398bdbe41,
0xe264589a4dcdab14,0xc696963c7eed2dd1,
0x8d7eb76070a08aec,0xfc1e1de5cf543ca2,
0xb0de65388cc8ada8,0x3b25a55f43294bcb,
0xdd15fe86affad912,0x49ef0eb713f39ebe,
0x8a2dbf142dfcc7ab,0x6e3569326c784337,
0xacb92ed9397bf996,0x49c2c37f07965404,
0xd7e77a8f87daf7fb,0xdc33745ec97be906,
0x86f0ac99b4e8dafd,0x69a028bb3ded71a3,
0xa8acd7c0222311bc,0xc40832ea0d68ce0c,
0xd2d80db02aabd62b,0xf50a3fa490c30190,
0x83c7088e1aab65db,0x792667c6da79e0fa,
0xa4b8cab1a1563f52,0x577001b891185938,
0xcde6fd5e09abcf26,0xed4c0226b55e6f86,
0x80b05e5ac60b6178,0x544f8158315b05b4,
0xa0dc75f1778e39d6,0x696361ae3db1c721,
0xc913936dd571c84c,0x3bc3a19cd1e38e9,
0xfb5878494ace3a5f,0x4ab48a04065c723,
0x9d174b2dcec0e47b,0x62eb0d64283f9c76,
0xc45d1df942711d9a,0x3ba5d0bd324f8394,
0xf5746577930d6500,0xca8f44ec7ee36479,
0x9968bf6abbe85f20,0x7e998b13cf4e1ecb,
0xbfc2ef456ae276e8,0x9e3fedd8c321a67e,
0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e,
0x95d04aee3b80ece5,0xbba1f1d158724a12,
0xbb445da9ca61281f,0x2a8a6e45ae8edc97,
0xea1575143cf97226,0xf52d09d71a3293bd,
0x924d692ca61be758,0x593c2626705f9c56,
0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c,
0xe498f455c38b997a,0xb6dfb9c0f956447,
0x8edf98b59a373fec,0x4724bd4189bd5eac,
0xb2977ee300c50fe7,0x58edec91ec2cb657,
0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed,
0x8b865b215899f46c,0xbd79e0d20082ee74,
0xae67f1e9aec07187,0xecd8590680a3aa11,
0xda01ee641a708de9,0xe80e6f4820cc9495,
0x884134fe908658b2,0x3109058d147fdcdd,
0xaa51823e34a7eede,0xbd4b46f0599fd415,
0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a,
0x850fadc09923329e,0x3e2cf6bc604ddb0,
0xa6539930bf6bff45,0x84db8346b786151c,
0xcfe87f7cef46ff16,0xe612641865679a63,
0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e,
0xa26da3999aef7749,0xe3be5e330f38f09d,
0xcb090c8001ab551c,0x5cadf5bfd3072cc5,
0xfdcb4fa002162a63,0x73d9732fc7c8f7f6,
0x9e9f11c4014dda7e,0x2867e7fddcdd9afa,
0xc646d63501a1511d,0xb281e1fd541501b8,
0xf7d88bc24209a565,0x1f225a7ca91a4226,
0x9ae757596946075f,0x3375788de9b06958,
0xc1a12d2fc3978937,0x52d6b1641c83ae,
0xf209787bb47d6b84,0xc0678c5dbd23a49a,
0x9745eb4d50ce6332,0xf840b7ba963646e0,
0xbd176620a501fbff,0xb650e5a93bc3d898,
0xec5d3fa8ce427aff,0xa3e51f138ab4cebe,
0x93ba47c980e98cdf,0xc66f336c36b10137,
0xb8a8d9bbe123f017,0xb80b0047445d4184,
0xe6d3102ad96cec1d,0xa60dc059157491e5,
0x9043ea1ac7e41392,0x87c89837ad68db2f,
0xb454e4a179dd1877,0x29babe4598c311fb,
0xe16a1dc9d8545e94,0xf4296dd6fef3d67a,
0x8ce2529e2734bb1d,0x1899e4a65f58660c,
0xb01ae745b101e9e4,0x5ec05dcff72e7f8f,
0xdc21a1171d42645d,0x76707543f4fa1f73,
0x899504ae72497eba,0x6a06494a791c53a8,
0xabfa45da0edbde69,0x487db9d17636892,
0xd6f8d7509292d603,0x45a9d2845d3c42b6,
0x865b86925b9bc5c2,0xb8a2392ba45a9b2,
0xa7f26836f282b732,0x8e6cac7768d7141e,
0xd1ef0244af2364ff,0x3207d795430cd926,
0x8335616aed761f1f,0x7f44e6bd49e807b8,
0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6,
0xcd036837130890a1,0x36dba887c37a8c0f,
0x802221226be55a64,0xc2494954da2c9789,
0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c,
0xc83553c5c8965d3d,0x6f92829494e5acc7,
0xfa42a8b73abbf48c,0xcb772339ba1f17f9,
0x9c69a97284b578d7,0xff2a760414536efb,
0xc38413cf25e2d70d,0xfef5138519684aba,
0xf46518c2ef5b8cd1,0x7eb258665fc25d69,
0x98bf2f79d5993802,0xef2f773ffbd97a61,
0xbeeefb584aff8603,0xaafb550ffacfd8fa,
0xeeaaba2e5dbf6784,0x95ba2a53f983cf38,
0x952ab45cfa97a0b2,0xdd945a747bf26183,
0xba756174393d88df,0x94f971119aeef9e4,
0xe912b9d1478ceb17,0x7a37cd5601aab85d,
0x91abb422ccb812ee,0xac62e055c10ab33a,
0xb616a12b7fe617aa,0x577b986b314d6009,
0xe39c49765fdf9d94,0xed5a7e85fda0b80b,
0x8e41ade9fbebc27d,0x14588f13be847307,
0xb1d219647ae6b31c,0x596eb2d8ae258fc8,
0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb,
0x8aec23d680043bee,0x25de7bb9480d5854,
0xada72ccc20054ae9,0xaf561aa79a10ae6a,
0xd910f7ff28069da4,0x1b2ba1518094da04,
0x87aa9aff79042286,0x90fb44d2f05d0842,
0xa99541bf57452b28,0x353a1607ac744a53,
0xd3fa922f2d1675f2,0x42889b8997915ce8,
0x847c9b5d7c2e09b7,0x69956135febada11,
0xa59bc234db398c25,0x43fab9837e699095,
0xcf02b2c21207ef2e,0x94f967e45e03f4bb,
0x8161afb94b44f57d,0x1d1be0eebac278f5,
0xa1ba1ba79e1632dc,0x6462d92a69731732,
0xca28a291859bbf93,0x7d7b8f7503cfdcfe,
0xfcb2cb35e702af78,0x5cda735244c3d43e,
0x9defbf01b061adab,0x3a0888136afa64a7,
0xc56baec21c7a1916,0x88aaa1845b8fdd0,
0xf6c69a72a3989f5b,0x8aad549e57273d45,
0x9a3c2087a63f6399,0x36ac54e2f678864b,
0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd,
0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5,
0x969eb7c47859e743,0x9f644ae5a4b1b325,
0xbc4665b596706114,0x873d5d9f0dde1fee,
0xeb57ff22fc0c7959,0xa90cb506d155a7ea,
0x9316ff75dd87cbd8,0x9a7f12442d588f2,
0xb7dcbf5354e9bece,0xc11ed6d538aeb2f,
0xe5d3ef282a242e81,0x8f1668c8a86da5fa,
0x8fa475791a569d10,0xf96e017d694487bc,
0xb38d92d760ec4455,0x37c981dcc395a9ac,
0xe070f78d3927556a,0x85bbe253f47b1417,
0x8c469ab843b89562,0x93956d7478ccec8e,
0xaf58416654a6babb,0x387ac8d1970027b2,
0xdb2e51bfe9d0696a,0x6997b05fcc0319e,
0x88fcf317f22241e2,0x441fece3bdf81f03,
0xab3c2fddeeaad25a,0xd527e81cad7626c3,
0xd60b3bd56a5586f1,0x8a71e223d8d3b074,
0x85c7056562757456,0xf6872d5667844e49,
0xa738c6bebb12d16c,0xb428f8ac016561db,
0xd106f86e69d785c7,0xe13336d701beba52,
0x82a45b450226b39c,0xecc0024661173473,
0xa34d721642b06084,0x27f002d7f95d0190,
0xcc20ce9bd35c78a5,0x31ec038df7b441f4,
0xff290242c83396ce,0x7e67047175a15271,
0x9f79a169bd203e41,0xf0062c6e984d386,
0xc75809c42c684dd1,0x52c07b78a3e60868,
0xf92e0c3537826145,0xa7709a56ccdf8a82,
0x9bbcc7a142b17ccb,0x88a66076400bb691,
0xc2abf989935ddbfe,0x6acff893d00ea435,
0xf356f7ebf83552fe,0x583f6b8c4124d43,
0x98165af37b2153de,0xc3727a337a8b704a,
0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c,
0xeda2ee1c7064130c,0x1162def06f79df73,
0x9485d4d1c63e8be7,0x8addcb5645ac2ba8,
0xb9a74a0637ce2ee1,0x6d953e2bd7173692,
0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437,
0x910ab1d4db9914a0,0x1d9c9892400a22a2,
0xb54d5e4a127f59c8,0x2503beb6d00cab4b,
0xe2a0b5dc971f303a,0x2e44ae64840fd61d,
0x8da471a9de737e24,0x5ceaecfed289e5d2,
0xb10d8e1456105dad,0x7425a83e872c5f47,
0xdd50f1996b947518,0xd12f124e28f77719,
0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f,
0xace73cbfdc0bfb7b,0x636cc64d1001550b,
0xd8210befd30efa5a,0x3c47f7e05401aa4e,
0x8714a775e3e95c78,0x65acfaec34810a71,
0xa8d9d1535ce3b396,0x7f1839a741a14d0d,
0xd31045a8341ca07c,0x1ede48111209a050,
0x83ea2b892091e44d,0x934aed0aab460432,
0xa4e4b66b68b65d60,0xf81da84d5617853f,
0xce1de40642e3f4b9,0x36251260ab9d668e,
0x80d2ae83e9ce78f3,0xc1d72b7c6b426019,
0xa1075a24e4421730,0xb24cf65b8612f81f,
0xc94930ae1d529cfc,0xdee033f26797b627,
0xfb9b7cd9a4a7443c,0x169840ef017da3b1,
0x9d412e0806e88aa5,0x8e1f289560ee864e,
0xc491798a08a2ad4e,0xf1a6f2bab92a27e2,
0xf5b5d7ec8acb58a2,0xae10af696774b1db,
0x9991a6f3d6bf1765,0xacca6da1e0a8ef29,
0xbff610b0cc6edd3f,0x17fd090a58d32af3,
0xeff394dcff8a948e,0xddfc4b4cef07f5b0,
0x95f83d0a1fb69cd9,0x4abdaf101564f98e,
0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1,
0xea53df5fd18d5513,0x84c86189216dc5ed,
0x92746b9be2f8552c,0x32fd3cf5b4e49bb4,
0xb7118682dbb66a77,0x3fbc8c33221dc2a1,
0xe4d5e82392a40515,0xfabaf3feaa5334a,
0x8f05b1163ba6832d,0x29cb4d87f2a7400e,
0xb2c71d5bca9023f8,0x743e20e9ef511012,
0xdf78e4b2bd342cf6,0x914da9246b255416,
0x8bab8eefb6409c1a,0x1ad089b6c2f7548e,
0xae9672aba3d0c320,0xa184ac2473b529b1,
0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e,
0x8865899617fb1871,0x7e2fa67c7a658892,
0xaa7eebfb9df9de8d,0xddbb901b98feeab7,
0xd51ea6fa85785631,0x552a74227f3ea565,
0x8533285c936b35de,0xd53a88958f87275f,
0xa67ff273b8460356,0x8a892abaf368f137,
0xd01fef10a657842c,0x2d2b7569b0432d85,
0x8213f56a67f6b29b,0x9c3b29620e29fc73,
0xa298f2c501f45f42,0x8349f3ba91b47b8f,
0xcb3f2f7642717713,0x241c70a936219a73,
0xfe0efb53d30dd4d7,0xed238cd383aa0110,
0x9ec95d1463e8a506,0xf4363804324a40aa,
0xc67bb4597ce2ce48,0xb143c6053edcd0d5,
0xf81aa16fdc1b81da,0xdd94b7868e94050a,
0x9b10a4e5e9913128,0xca7cf2b4191c8326,
0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0,
0xf24a01a73cf2dccf,0xbc633b39673c8cec,
0x976e41088617ca01,0xd5be0503e085d813,
0xbd49d14aa79dbc82,0x4b2d8644d8a74e18,
0xec9c459d51852ba2,0xddf8e7d60ed1219e,
0x93e1ab8252f33b45,0xcabb90e5c942b503,
0xb8da1662e7b00a17,0x3d6a751f3b936243,
0xe7109bfba19c0c9d,0xcc512670a783ad4,
0x906a617d450187e2,0x27fb2b80668b24c5,
0xb484f9dc9641e9da,0xb1f9f660802dedf6,
0xe1a63853bbd26451,0x5e7873f8a0396973,
0x8d07e33455637eb2,0xdb0b487b6423e1e8,
0xb049dc016abc5e5f,0x91ce1a9a3d2cda62,
0xdc5c5301c56b75f7,0x7641a140cc7810fb,
0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d,
0xac2820d9623bf429,0x546345fa9fbdcd44,
0xd732290fbacaf133,0xa97c177947ad4095,
0x867f59a9d4bed6c0,0x49ed8eabcccc485d,
0xa81f301449ee8c70,0x5c68f256bfff5a74,
0xd226fc195c6a2f8c,0x73832eec6fff3111,
0x83585d8fd9c25db7,0xc831fd53c5ff7eab,
0xa42e74f3d032f525,0xba3e7ca8b77f5e55,
0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb,
0x80444b5e7aa7cf85,0x7980d163cf5b81b3,
0xa0555e361951c366,0xd7e105bcc332621f,
0xc86ab5c39fa63440,0x8dd9472bf3fefaa7,
0xfa856334878fc150,0xb14f98f6f0feb951,
0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3,
0xc3b8358109e84f07,0xa862f80ec4700c8,
0xf4a642e14c6262c8,0xcd27bb612758c0fa,
0x98e7e9cccfbd7dbd,0x8038d51cb897789c,
0xbf21e44003acdd2c,0xe0470a63e6bd56c3,
0xeeea5d5004981478,0x1858ccfce06cac74,
0x95527a5202df0ccb,0xf37801e0c43ebc8,
0xbaa718e68396cffd,0xd30560258f54e6ba,
0xe950df20247c83fd,0x47c6b82ef32a2069,
0x91d28b7416cdd27e,0x4cdc331d57fa5441,
0xb6472e511c81471d,0xe0133fe4adf8e952,
0xe3d8f9e563a198e5,0x58180fddd97723a6,
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
};
template <class unused>
constexpr uint64_t powers_template<unused>::power_of_five_128[number_of_entries];
using powers = powers_template<>;
} // namespace fast_float
#endif

View File

@ -0,0 +1,670 @@
#ifndef FASTFLOAT_FLOAT_COMMON_H
#define FASTFLOAT_FLOAT_COMMON_H
#include <cfloat>
#include <cstdint>
#include <cassert>
#include <cstring>
#include <type_traits>
#include <system_error>
#include "constexpr_feature_detect.h"
namespace fast_float {
enum chars_format {
scientific = 1 << 0,
fixed = 1 << 2,
hex = 1 << 3,
general = fixed | scientific
};
template <typename UC>
struct from_chars_result_t {
UC const* ptr;
std::errc ec;
};
using from_chars_result = from_chars_result_t<char>;
template <typename UC>
struct parse_options_t {
constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
UC dot = UC('.'))
: format(fmt), decimal_point(dot) {}
/** Which number formats are accepted */
chars_format format;
/** The character used as decimal point */
UC decimal_point;
};
using parse_options = parse_options_t<char>;
}
#if FASTFLOAT_HAS_BIT_CAST
#include <bit>
#endif
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
|| defined(__MINGW64__) \
|| defined(__s390x__) \
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \
|| defined(__loongarch64) )
#define FASTFLOAT_64BIT 1
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
|| defined(__arm__) || defined(_M_ARM) || defined(__ppc__) \
|| defined(__MINGW32__) || defined(__EMSCRIPTEN__))
#define FASTFLOAT_32BIT 1
#else
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
// We can never tell the register width, but the SIZE_MAX is a good approximation.
// UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability.
#if SIZE_MAX == 0xffff
#error Unknown platform (16-bit, unsupported)
#elif SIZE_MAX == 0xffffffff
#define FASTFLOAT_32BIT 1
#elif SIZE_MAX == 0xffffffffffffffff
#define FASTFLOAT_64BIT 1
#else
#error Unknown platform (not 32-bit, not 64-bit?)
#endif
#endif
#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__))
#include <intrin.h>
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#define FASTFLOAT_VISUAL_STUDIO 1
#endif
#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#elif defined _WIN32
#define FASTFLOAT_IS_BIG_ENDIAN 0
#else
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <machine/endian.h>
#elif defined(sun) || defined(__sun)
#include <sys/byteorder.h>
#elif defined(__MVS__)
#include <sys/endian.h>
#else
#ifdef __has_include
#if __has_include(<endian.h>)
#include <endian.h>
#endif //__has_include(<endian.h>)
#endif //__has_include
#endif
#
#ifndef __BYTE_ORDER__
// safe choice
#define FASTFLOAT_IS_BIG_ENDIAN 0
#endif
#
#ifndef __ORDER_LITTLE_ENDIAN__
// safe choice
#define FASTFLOAT_IS_BIG_ENDIAN 0
#endif
#
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define FASTFLOAT_IS_BIG_ENDIAN 0
#else
#define FASTFLOAT_IS_BIG_ENDIAN 1
#endif
#endif
#if defined(__SSE2__) || \
(defined(FASTFLOAT_VISUAL_STUDIO) && \
(defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2)))
#define FASTFLOAT_SSE2 1
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#define FASTFLOAT_NEON 1
#endif
#if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_ARM64)
#define FASTFLOAT_HAS_SIMD 1
#endif
#if defined(__GNUC__)
// disable -Wcast-align=strict (GCC only)
#define FASTFLOAT_SIMD_DISABLE_WARNINGS \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wcast-align\"")
#else
#define FASTFLOAT_SIMD_DISABLE_WARNINGS
#endif
#if defined(__GNUC__)
#define FASTFLOAT_SIMD_RESTORE_WARNINGS \
_Pragma("GCC diagnostic pop")
#else
#define FASTFLOAT_SIMD_RESTORE_WARNINGS
#endif
#ifdef FASTFLOAT_VISUAL_STUDIO
#define fastfloat_really_inline __forceinline
#else
#define fastfloat_really_inline inline __attribute__((always_inline))
#endif
#ifndef FASTFLOAT_ASSERT
#define FASTFLOAT_ASSERT(x) { ((void)(x)); }
#endif
#ifndef FASTFLOAT_DEBUG_ASSERT
#define FASTFLOAT_DEBUG_ASSERT(x) { ((void)(x)); }
#endif
// rust style `try!()` macro, or `?` operator
#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
#define FASTFLOAT_ENABLE_IF(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0
namespace fast_float {
fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
return std::is_constant_evaluated();
#else
return false;
#endif
}
// Compares two ASCII strings in a case insensitive manner.
template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(UC const * input1, UC const * input2, size_t length) {
char running_diff{0};
for (size_t i = 0; i < length; ++i) {
running_diff |= (char(input1[i]) ^ char(input2[i]));
}
return (running_diff == 0) || (running_diff == 32);
}
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
// a pointer and a length to a contiguous block of memory
template <typename T>
struct span {
const T* ptr;
size_t length;
constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
constexpr span() : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept {
return length;
}
FASTFLOAT_CONSTEXPR14 const T& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return ptr[index];
}
};
struct value128 {
uint64_t low;
uint64_t high;
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
constexpr value128() : low(0), high(0) {}
};
/* Helper C++14 constexpr generic implementation of leading_zeroes */
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
int leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
if(input_num & uint64_t(0xffffffff00000000)) { input_num >>= 32; last_bit |= 32; }
if(input_num & uint64_t( 0xffff0000)) { input_num >>= 16; last_bit |= 16; }
if(input_num & uint64_t( 0xff00)) { input_num >>= 8; last_bit |= 8; }
if(input_num & uint64_t( 0xf0)) { input_num >>= 4; last_bit |= 4; }
if(input_num & uint64_t( 0xc)) { input_num >>= 2; last_bit |= 2; }
if(input_num & uint64_t( 0x2)) { input_num >>= 1; last_bit |= 1; }
return 63 - last_bit;
}
/* result might be undefined when input_num is zero */
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
int leading_zeroes(uint64_t input_num) {
assert(input_num > 0);
if (cpp20_and_in_constexpr()) {
return leading_zeroes_generic(input_num);
}
#ifdef FASTFLOAT_VISUAL_STUDIO
#if defined(_M_X64) || defined(_M_ARM64)
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
_BitScanReverse64(&leading_zero, input_num);
return (int)(63 - leading_zero);
#else
return leading_zeroes_generic(input_num);
#endif
#else
return __builtin_clzll(input_num);
#endif
}
// slow emulation routine for 32-bit
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#ifdef FASTFLOAT_32BIT
// slow emulation routine for 32-bit
#if !defined(__MINGW64__)
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
return umul128_generic(ab, cd, hi);
}
#endif // !__MINGW64__
#endif // FASTFLOAT_32BIT
// compute 64-bit a*b
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
value128 full_multiplication(uint64_t a, uint64_t b) {
if (cpp20_and_in_constexpr()) {
value128 answer;
answer.low = umul128_generic(a, b, &answer.high);
return answer;
}
value128 answer;
#if defined(_M_ARM64) && !defined(__MINGW32__)
// ARM64 has native support for 64-bit multiplications, no need to emulate
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications
answer.high = __umulh(a, b);
answer.low = a * b;
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
#elif defined(FASTFLOAT_64BIT)
__uint128_t r = ((__uint128_t)a) * b;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#else
answer.low = umul128_generic(a, b, &answer.high);
#endif
return answer;
}
struct adjusted_mantissa {
uint64_t mantissa{0};
int32_t power2{0}; // a negative value indicates an invalid result
adjusted_mantissa() = default;
constexpr bool operator==(const adjusted_mantissa &o) const {
return mantissa == o.mantissa && power2 == o.power2;
}
constexpr bool operator!=(const adjusted_mantissa &o) const {
return mantissa != o.mantissa || power2 != o.power2;
}
};
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
constexpr static int32_t invalid_am_bias = -0x8000;
// used for binary_format_lookup_tables<T>::max_mantissa
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
template <typename T, typename U = void>
struct binary_format_lookup_tables;
template <typename T> struct binary_format : binary_format_lookup_tables<T> {
using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
static inline constexpr int mantissa_explicit_bits();
static inline constexpr int minimum_exponent();
static inline constexpr int infinite_power();
static inline constexpr int sign_index();
static inline constexpr int min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
static inline constexpr int max_exponent_fast_path();
static inline constexpr int max_exponent_round_to_even();
static inline constexpr int min_exponent_round_to_even();
static inline constexpr uint64_t max_mantissa_fast_path(int64_t power);
static inline constexpr uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
static inline constexpr int largest_power_of_ten();
static inline constexpr int smallest_power_of_ten();
static inline constexpr T exact_power_of_ten(int64_t power);
static inline constexpr size_t max_digits();
static inline constexpr equiv_uint exponent_mask();
static inline constexpr equiv_uint mantissa_mask();
static inline constexpr equiv_uint hidden_bit_mask();
};
template <typename U>
struct binary_format_lookup_tables<double, U> {
static constexpr double powers_of_ten[] = {
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
// Largest integer value v so that (5**index * v) <= 1<<53.
// 0x10000000000000 == 1 << 53
static constexpr uint64_t max_mantissa[] = {
0x10000000000000,
0x10000000000000 / 5,
0x10000000000000 / (5 * 5),
0x10000000000000 / (5 * 5 * 5),
0x10000000000000 / (5 * 5 * 5 * 5),
0x10000000000000 / (constant_55555),
0x10000000000000 / (constant_55555 * 5),
0x10000000000000 / (constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * 5 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555),
0x10000000000000 / (constant_55555 * constant_55555 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)};
};
template <typename U>
constexpr double binary_format_lookup_tables<double, U>::powers_of_ten[];
template <typename U>
constexpr uint64_t binary_format_lookup_tables<double, U>::max_mantissa[];
template <typename U>
struct binary_format_lookup_tables<float, U> {
static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// Largest integer value v so that (5**index * v) <= 1<<24.
// 0x1000000 == 1<<24
static constexpr uint64_t max_mantissa[] = {
0x1000000,
0x1000000 / 5,
0x1000000 / (5 * 5),
0x1000000 / (5 * 5 * 5),
0x1000000 / (5 * 5 * 5 * 5),
0x1000000 / (constant_55555),
0x1000000 / (constant_55555 * 5),
0x1000000 / (constant_55555 * 5 * 5),
0x1000000 / (constant_55555 * 5 * 5 * 5),
0x1000000 / (constant_55555 * 5 * 5 * 5 * 5),
0x1000000 / (constant_55555 * constant_55555),
0x1000000 / (constant_55555 * constant_55555 * 5)};
};
template <typename U>
constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
template <typename U>
constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[];
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -22;
#endif
}
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -10;
#endif
}
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
return 52;
}
template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() {
return 23;
}
template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() {
return 23;
}
template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() {
return 10;
}
template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() {
return -4;
}
template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() {
return -17;
}
template <> inline constexpr int binary_format<double>::minimum_exponent() {
return -1023;
}
template <> inline constexpr int binary_format<float>::minimum_exponent() {
return -127;
}
template <> inline constexpr int binary_format<double>::infinite_power() {
return 0x7FF;
}
template <> inline constexpr int binary_format<float>::infinite_power() {
return 0xFF;
}
template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
return 22;
}
template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
return 10;
}
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 22
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 10
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}
template <>
inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}
template <>
inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}
template <>
inline constexpr int binary_format<double>::largest_power_of_ten() {
return 308;
}
template <>
inline constexpr int binary_format<float>::largest_power_of_ten() {
return 38;
}
template <>
inline constexpr int binary_format<double>::smallest_power_of_ten() {
return -342;
}
template <>
inline constexpr int binary_format<float>::smallest_power_of_ten() {
return -65;
}
template <> inline constexpr size_t binary_format<double>::max_digits() {
return 769;
}
template <> inline constexpr size_t binary_format<float>::max_digits() {
return 114;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::exponent_mask() {
return 0x7F800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::exponent_mask() {
return 0x7FF0000000000000;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::mantissa_mask() {
return 0x007FFFFF;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::mantissa_mask() {
return 0x000FFFFFFFFFFFFF;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::hidden_bit_mask() {
return 0x00800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::hidden_bit_mask() {
return 0x0010000000000000;
}
template<typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void to_float(bool negative, adjusted_mantissa am, T &value) {
using fastfloat_uint = typename binary_format<T>::equiv_uint;
fastfloat_uint word = (fastfloat_uint)am.mantissa;
word |= fastfloat_uint(am.power2) << binary_format<T>::mantissa_explicit_bits();
word |= fastfloat_uint(negative) << binary_format<T>::sign_index();
#if FASTFLOAT_HAS_BIT_CAST
value = std::bit_cast<T>(word);
#else
::memcpy(&value, &word, sizeof(T));
#endif
}
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
template <typename = void>
struct space_lut {
static constexpr bool value[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
};
template <typename T>
constexpr bool space_lut<T>::value[];
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
#endif
template<typename UC>
static constexpr uint64_t int_cmp_zeros()
{
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size");
return (sizeof(UC) == 1) ? 0x3030303030303030 : (sizeof(UC) == 2) ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 | uint64_t(UC('0')) << 16 | UC('0')) : (uint64_t(UC('0')) << 32 | UC('0'));
}
template<typename UC>
static constexpr int int_cmp_len()
{
return sizeof(uint64_t) / sizeof(UC);
}
template<typename UC>
static constexpr UC const * str_const_nan()
{
return nullptr;
}
template<>
constexpr char const * str_const_nan<char>()
{
return "nan";
}
template<>
constexpr wchar_t const * str_const_nan<wchar_t>()
{
return L"nan";
}
template<>
constexpr char16_t const * str_const_nan<char16_t>()
{
return u"nan";
}
template<>
constexpr char32_t const * str_const_nan<char32_t>()
{
return U"nan";
}
template<typename UC>
static constexpr UC const * str_const_inf()
{
return nullptr;
}
template<>
constexpr char const * str_const_inf<char>()
{
return "infinity";
}
template<>
constexpr wchar_t const * str_const_inf<wchar_t>()
{
return L"infinity";
}
template<>
constexpr char16_t const * str_const_inf<char16_t>()
{
return u"infinity";
}
template<>
constexpr char32_t const * str_const_inf<char32_t>()
{
return U"infinity";
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,231 @@
#ifndef FASTFLOAT_PARSE_NUMBER_H
#define FASTFLOAT_PARSE_NUMBER_H
#include "ascii_number.h"
#include "decimal_to_binary.h"
#include "digit_comparison.h"
#include "float_common.h"
#include <cmath>
#include <cstring>
#include <limits>
#include <system_error>
namespace fast_float {
namespace detail {
/**
* Special case +inf, -inf, nan, infinity, -infinity.
* The case comparisons could be made much faster given that we know that the
* strings a null-free and fixed.
**/
template <typename T, typename UC>
from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14
parse_infnan(UC const * first, UC const * last, T &value) noexcept {
from_chars_result_t<UC> answer{};
answer.ptr = first;
answer.ec = std::errc(); // be optimistic
bool minusSign = false;
if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
minusSign = true;
++first;
}
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if (*first == UC('+')) {
++first;
}
#endif
if (last - first >= 3) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
answer.ptr = (first += 3);
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
if(first != last && *first == UC('(')) {
for(UC const * ptr = first + 1; ptr != last; ++ptr) {
if (*ptr == UC(')')) {
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
break;
}
else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
break; // forbidden char, not nan(n-char-seq-opt)
}
}
return answer;
}
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
answer.ptr = first + 8;
} else {
answer.ptr = first + 3;
}
value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
return answer;
}
}
answer.ec = std::errc::invalid_argument;
return answer;
}
/**
* Returns true if the floating-pointing rounding mode is to 'nearest'.
* It is the default on most system. This function is meant to be inexpensive.
* Credit : @mwalcott3
*/
fastfloat_really_inline bool rounds_to_nearest() noexcept {
// https://lemire.me/blog/2020/06/26/gcc-not-nearest/
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return false;
#endif
// See
// A fast function to check your floating-point rounding mode
// https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
//
// This function is meant to be equivalent to :
// prior: #include <cfenv>
// return fegetround() == FE_TONEAREST;
// However, it is expected to be much faster than the fegetround()
// function call.
//
// The volatile keywoard prevents the compiler from computing the function
// at compile-time.
// There might be other ways to prevent compile-time optimizations (e.g., asm).
// The value does not need to be std::numeric_limits<float>::min(), any small
// value so that 1 + x should round to 1 would do (after accounting for excess
// precision, as in 387 instructions).
static volatile float fmin = std::numeric_limits<float>::min();
float fmini = fmin; // we copy it so that it gets loaded at most once.
//
// Explanation:
// Only when fegetround() == FE_TONEAREST do we have that
// fmin + 1.0f == 1.0f - fmin.
//
// FE_UPWARD:
// fmin + 1.0f > 1
// 1.0f - fmin == 1
//
// FE_DOWNWARD or FE_TOWARDZERO:
// fmin + 1.0f == 1
// 1.0f - fmin < 1
//
// Note: This may fail to be accurate if fast-math has been
// enabled, as rounding conventions may not apply.
#ifdef FASTFLOAT_VISUAL_STUDIO
# pragma warning(push)
// todo: is there a VS warning?
// see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
#elif defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wfloat-equal"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
return (fmini + 1.0f == 1.0f - fmini);
#ifdef FASTFLOAT_VISUAL_STUDIO
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
} // namespace detail
template<typename T, typename UC>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_advanced(first, last, value, parse_options_t<UC>{fmt});
}
template<typename T, typename UC>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
T &value, parse_options_t<UC> options) noexcept {
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
static_assert (std::is_same<UC, char>::value ||
std::is_same<UC, wchar_t>::value ||
std::is_same<UC, char16_t>::value ||
std::is_same<UC, char32_t>::value , "only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
first++;
}
#endif
if (first == last) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
if (!pns.valid) {
return detail::parse_infnan(first, last, value);
}
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
// The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread.
// We proceed optimistically, assuming that detail::rounds_to_nearest() returns
// true.
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
// Unfortunately, the conventional Clinger's fast path is only possible
// when the system rounds to the nearest float.
//
// We expect the next branch to almost always be selected.
// We could check it first (before the previous branch), but
// there might be performance advantages at having the check
// be last.
if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path.
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
value = T(pns.mantissa);
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
if (pns.negative) { value = -value; }
return answer;
}
} else {
// We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal
if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
#if defined(__clang__)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if(pns.mantissa == 0) {
value = pns.negative ? -0. : 0.;
return answer;
}
#endif
value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
if (pns.negative) { value = -value; }
return answer;
}
}
}
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if(pns.too_many_digits && am.power2 >= 0) {
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
}
}
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
// then we need to go the long way around again. This is very uncommon.
if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
to_float(pns.negative, am, value);
// Test for over/underflow.
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format<T>::infinite_power()) {
answer.ec = std::errc::result_out_of_range;
}
return answer;
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,360 @@
#ifndef FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H
#define FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H
/**
* This code is meant to handle the case where we have more than 19 digits.
*
* It is based on work by Nigel Tao (at https://github.com/google/wuffs/)
* who credits Ken Thompson for the design (via a reference to the Go source
* code).
*
* Rob Pike suggested that this algorithm be called "Simple Decimal Conversion".
*
* It is probably not very fast but it is a fallback that should almost never
* be used in real life. Though it is not fast, it is "easily" understood and debugged.
**/
#include "ascii_number.h"
#include "decimal_to_binary.h"
#include <cstdint>
namespace fast_float {
namespace detail {
// remove all final zeroes
inline void trim(decimal &h) {
while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) {
h.num_digits--;
}
}
inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t shift) {
shift &= 63;
constexpr uint16_t number_of_digits_decimal_left_shift_table[65] = {
0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817,
0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067,
0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF,
0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0,
0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA,
0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC,
0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C,
0x051C, 0x051C,
};
uint32_t x_a = number_of_digits_decimal_left_shift_table[shift];
uint32_t x_b = number_of_digits_decimal_left_shift_table[shift + 1];
uint32_t num_new_digits = x_a >> 11;
uint32_t pow5_a = 0x7FF & x_a;
uint32_t pow5_b = 0x7FF & x_b;
constexpr uint8_t
number_of_digits_decimal_left_shift_table_powers_of_5[0x051C] = {
5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3,
9, 0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8,
1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1,
0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, 5, 2, 5, 8,
7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6,
9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5,
3, 6, 7, 4, 3, 1, 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3,
1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0,
9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6, 4, 4, 7, 7, 5, 3,
9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1,
4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8,
0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4,
6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6, 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5,
7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5,
6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2,
5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5,
1, 1, 6, 4, 1, 5, 3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5,
5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5,
2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, 1, 3, 2, 8, 1, 2,
5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0,
6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2,
0, 3, 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1,
6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6,
4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7, 2,
9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5,
0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7,
3, 7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5,
6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9,
3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, 8, 6, 0, 8, 0, 8,
0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0,
9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2,
5, 1, 4, 2, 1, 0, 8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2,
4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0,
0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7,
1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8,
9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4,
6, 7, 7, 8, 1, 0, 6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1,
9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3, 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5,
6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9,
4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4,
9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4,
0, 6, 2, 5, 1, 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4,
2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1,
5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, 8, 3, 4, 0, 4, 5,
4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1,
3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1,
3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9,
5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0,
3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, 7, 6, 2,
6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1,
8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5,
1, 7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2,
4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1,
7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, 2, 4, 0, 6, 9, 5,
9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5,
};
const uint8_t *pow5 =
&number_of_digits_decimal_left_shift_table_powers_of_5[pow5_a];
uint32_t i = 0;
uint32_t n = pow5_b - pow5_a;
for (; i < n; i++) {
if (i >= h.num_digits) {
return num_new_digits - 1;
} else if (h.digits[i] == pow5[i]) {
continue;
} else if (h.digits[i] < pow5[i]) {
return num_new_digits - 1;
} else {
return num_new_digits;
}
}
return num_new_digits;
}
inline uint64_t round(decimal &h) {
if ((h.num_digits == 0) || (h.decimal_point < 0)) {
return 0;
} else if (h.decimal_point > 18) {
return UINT64_MAX;
}
// at this point, we know that h.decimal_point >= 0
uint32_t dp = uint32_t(h.decimal_point);
uint64_t n = 0;
for (uint32_t i = 0; i < dp; i++) {
n = (10 * n) + ((i < h.num_digits) ? h.digits[i] : 0);
}
bool round_up = false;
if (dp < h.num_digits) {
round_up = h.digits[dp] >= 5; // normally, we round up
// but we may need to round to even!
if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) {
round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1]));
}
}
if (round_up) {
n++;
}
return n;
}
// computes h * 2^-shift
inline void decimal_left_shift(decimal &h, uint32_t shift) {
if (h.num_digits == 0) {
return;
}
uint32_t num_new_digits = number_of_digits_decimal_left_shift(h, shift);
int32_t read_index = int32_t(h.num_digits - 1);
uint32_t write_index = h.num_digits - 1 + num_new_digits;
uint64_t n = 0;
while (read_index >= 0) {
n += uint64_t(h.digits[read_index]) << shift;
uint64_t quotient = n / 10;
uint64_t remainder = n - (10 * quotient);
if (write_index < max_digits) {
h.digits[write_index] = uint8_t(remainder);
} else if (remainder > 0) {
h.truncated = true;
}
n = quotient;
write_index--;
read_index--;
}
while (n > 0) {
uint64_t quotient = n / 10;
uint64_t remainder = n - (10 * quotient);
if (write_index < max_digits) {
h.digits[write_index] = uint8_t(remainder);
} else if (remainder > 0) {
h.truncated = true;
}
n = quotient;
write_index--;
}
h.num_digits += num_new_digits;
if (h.num_digits > max_digits) {
h.num_digits = max_digits;
}
h.decimal_point += int32_t(num_new_digits);
trim(h);
}
// computes h * 2^shift
inline void decimal_right_shift(decimal &h, uint32_t shift) {
uint32_t read_index = 0;
uint32_t write_index = 0;
uint64_t n = 0;
while ((n >> shift) == 0) {
if (read_index < h.num_digits) {
n = (10 * n) + h.digits[read_index++];
} else if (n == 0) {
return;
} else {
while ((n >> shift) == 0) {
n = 10 * n;
read_index++;
}
break;
}
}
h.decimal_point -= int32_t(read_index - 1);
if (h.decimal_point < -decimal_point_range) { // it is zero
h.num_digits = 0;
h.decimal_point = 0;
h.negative = false;
h.truncated = false;
return;
}
uint64_t mask = (uint64_t(1) << shift) - 1;
while (read_index < h.num_digits) {
uint8_t new_digit = uint8_t(n >> shift);
n = (10 * (n & mask)) + h.digits[read_index++];
h.digits[write_index++] = new_digit;
}
while (n > 0) {
uint8_t new_digit = uint8_t(n >> shift);
n = 10 * (n & mask);
if (write_index < max_digits) {
h.digits[write_index++] = new_digit;
} else if (new_digit > 0) {
h.truncated = true;
}
}
h.num_digits = write_index;
trim(h);
}
} // namespace detail
template <typename binary>
adjusted_mantissa compute_float(decimal &d) {
adjusted_mantissa answer;
if (d.num_digits == 0) {
// should be zero
answer.power2 = 0;
answer.mantissa = 0;
return answer;
}
// At this point, going further, we can assume that d.num_digits > 0.
//
// We want to guard against excessive decimal point values because
// they can result in long running times. Indeed, we do
// shifts by at most 60 bits. We have that log(10**400)/log(2**60) ~= 22
// which is fine, but log(10**299995)/log(2**60) ~= 16609 which is not
// fine (runs for a long time).
//
if(d.decimal_point < -324) {
// We have something smaller than 1e-324 which is always zero
// in binary64 and binary32.
// It should be zero.
answer.power2 = 0;
answer.mantissa = 0;
return answer;
} else if(d.decimal_point >= 310) {
// We have something at least as large as 0.1e310 which is
// always infinite.
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
constexpr uint32_t max_shift = 60;
constexpr uint32_t num_powers = 19;
constexpr uint8_t decimal_powers[19] = {
0, 3, 6, 9, 13, 16, 19, 23, 26, 29, //
33, 36, 39, 43, 46, 49, 53, 56, 59, //
};
int32_t exp2 = 0;
while (d.decimal_point > 0) {
uint32_t n = uint32_t(d.decimal_point);
uint32_t shift = (n < num_powers) ? decimal_powers[n] : max_shift;
detail::decimal_right_shift(d, shift);
if (d.decimal_point < -decimal_point_range) {
// should be zero
answer.power2 = 0;
answer.mantissa = 0;
return answer;
}
exp2 += int32_t(shift);
}
// We shift left toward [1/2 ... 1].
while (d.decimal_point <= 0) {
uint32_t shift;
if (d.decimal_point == 0) {
if (d.digits[0] >= 5) {
break;
}
shift = (d.digits[0] < 2) ? 2 : 1;
} else {
uint32_t n = uint32_t(-d.decimal_point);
shift = (n < num_powers) ? decimal_powers[n] : max_shift;
}
detail::decimal_left_shift(d, shift);
if (d.decimal_point > decimal_point_range) {
// we want to get infinity:
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
exp2 -= int32_t(shift);
}
// We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2].
exp2--;
constexpr int32_t minimum_exponent = binary::minimum_exponent();
while ((minimum_exponent + 1) > exp2) {
uint32_t n = uint32_t((minimum_exponent + 1) - exp2);
if (n > max_shift) {
n = max_shift;
}
detail::decimal_right_shift(d, n);
exp2 += int32_t(n);
}
if ((exp2 - minimum_exponent) >= binary::infinite_power()) {
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
const int mantissa_size_in_bits = binary::mantissa_explicit_bits() + 1;
detail::decimal_left_shift(d, mantissa_size_in_bits);
uint64_t mantissa = detail::round(d);
// It is possible that we have an overflow, in which case we need
// to shift back.
if(mantissa >= (uint64_t(1) << mantissa_size_in_bits)) {
detail::decimal_right_shift(d, 1);
exp2 += 1;
mantissa = detail::round(d);
if ((exp2 - minimum_exponent) >= binary::infinite_power()) {
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
}
answer.power2 = exp2 - binary::minimum_exponent();
if(mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) { answer.power2--; }
answer.mantissa = mantissa & ((uint64_t(1) << binary::mantissa_explicit_bits()) - 1);
return answer;
}
template <typename binary>
adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) {
decimal d = parse_decimal(first, last, options);
return compute_float<binary>(d);
}
} // namespace fast_float
#endif

View File

@ -0,0 +1,19 @@
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_fwd.hpp - The forwarding header for the header/implementation seperated usage of
// ghc::filesystem.
// This file can be include at any place, where ghc::filesystem api is needed while
// not bleeding implementation details (e.g. system includes) into the global namespace,
// as long as one cpp includes fs_impl.hpp to deliver the matching implementations.
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_FWD_H
#define GHC_FILESYSTEM_FWD_H
#define GHC_FILESYSTEM_FWD
#include <ghc/filesystem.hpp>
#endif // GHC_FILESYSTEM_FWD_H

View File

@ -0,0 +1,35 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_impl.hpp - The implementation header for the header/implementation seperated usage of
// ghc::filesystem.
// This file can be used to hide the implementation of ghc::filesystem into a single cpp.
// The cpp has to include this before including fs_fwd.hpp directly or via a different
// header to work.
//---------------------------------------------------------------------------------------
#define GHC_FILESYSTEM_IMPLEMENTATION
#include <ghc/filesystem.hpp>

View File

@ -0,0 +1,60 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_std.hpp - The dynamic switching header that includes std::filesystem if detected
// or ghc::filesystem if not, and makes the resulting API available in the
// namespace fs.
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_H
#define GHC_FILESYSTEM_STD_H
#if defined(__APPLE__)
#include <Availability.h>
#endif
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem>
namespace fs {
using namespace std::filesystem;
using ifstream = std::ifstream;
using ofstream = std::ofstream;
using fstream = std::fstream;
}
#endif
#endif
#ifndef GHC_USE_STD_FS
//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
#include <ghc/filesystem.hpp>
namespace fs {
using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif
#endif // GHC_FILESYSTEM_STD_H

View File

@ -0,0 +1,63 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_std_fwd.hpp - The forwarding header for the header/implementation seperated usage of
// ghc::filesystem that uses std::filesystem if it detects it.
// This file can be include at any place, where fs::filesystem api is needed while
// not bleeding implementation details (e.g. system includes) into the global namespace,
// as long as one cpp includes fs_std_impl.hpp to deliver the matching implementations.
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_FWD_H
#define GHC_FILESYSTEM_STD_FWD_H
#if defined(__APPLE__)
#include <Availability.h>
#endif
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem>
namespace fs {
using namespace std::filesystem;
using ifstream = std::ifstream;
using ofstream = std::ofstream;
using fstream = std::fstream;
}
#endif
#endif
#ifndef GHC_USE_STD_FS
//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
#define GHC_FILESYSTEM_FWD
#include <ghc/filesystem.hpp>
namespace fs {
using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif
#endif // GHC_FILESYSTEM_STD_FWD_H

View File

@ -0,0 +1,46 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_std_impl.hpp - The implementation header for the header/implementation seperated usage of
// ghc::filesystem that does nothing if std::filesystem is detected.
// This file can be used to hide the implementation of ghc::filesystem into a single cpp.
// The cpp has to include this before including fs_std_fwd.hpp directly or via a different
// header to work.
//---------------------------------------------------------------------------------------
#if defined(__APPLE__)
#include <Availability.h>
#endif
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#endif
#endif
#ifndef GHC_USE_STD_FS
//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
#define GHC_FILESYSTEM_IMPLEMENTATION
#include <ghc/filesystem.hpp>
#endif

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -0,0 +1,112 @@
floaxie
=======
[![Build Status in GCC/Clang](https://travis-ci.org/aclex/floaxie.svg?branch=master)](https://travis-ci.org/aclex/floaxie) [![Build status in Visual Studio](https://ci.appveyor.com/api/projects/status/nhidn1n2o66etirk?svg=true)](https://ci.appveyor.com/project/aclex/floaxie) [![Code coverage](https://codecov.io/gh/aclex/floaxie/branch/master/graph/badge.svg)](https://codecov.io/gh/aclex/floaxie)
Floaxie is C++14 header-only library for [fast](https://github.com/miloyip/dtoa-benchmark/) printing floating point values of arbitrary precision (`float`, `double` etc.) and parsing their textual representation back again (in ordinary or exponent notation).
What is it for?
---------------
One is obviously not worried too much about the speed of the values being printed on the console, so the primary places to use the library are readers, writers, encoders and decoders of different text-based formats (e.g. JSON, XML and so on), applications interacting through text pipes or rendering data structures.
Please, take a look also at the alternatives solving the same problem mentioned in the benchmark of @miloyip [here](https://github.com/miloyip/dtoa-benchmark/), if `floaxie` doesn't suite you well.
Compiler compatibility
----------------------
- [x] GCC 5
- [x] Clang 3.6
- [x] Visual Studio 2017 15.0
Printing
--------
**Grisu2** algorithm is adopted for printing floating point values. It is fast printing algorithm described by Florian Loitsch in his [Printing Floating-Point Numbers Quickly and Accurately with Integers](http://florian.loitsch.com/publications/dtoa-pldi2010.pdf) paper. **Grisu2** is chosen as probably the fastest **grisu** version, for cases, where shortest possible representation in 100% of cases is not ultimately important. Still it guarantees the best possible efficiency of more, than 99%.
Parsing
-------
The opposite to **Grisu** algorithm used for printing, an algorithm on the same theoretical base, but for parsing, is developed. Following the analogue of **Grisu** naming, who essentially appears to be cartoon character (Dragon), parsing algorithm is named after another animated character, rabbit, **Krosh:** ![Krosh](http://img4.wikia.nocookie.net/__cb20130427170555/smesharikiarhives/ru/images/0/03/%D0%9A%D1%80%D0%BE%D1%88.png "Krosh")
The algorithm parses decimal mantissa to extent of slightly more decimal digit capacity of floating point types, chooses a pre-calculated decimal power and then multiplies the two. Since the [rounding problem](http://www.exploringbinary.com/decimal-to-floating-point-needs-arbitrary-precision/) is not uncommon during such operations, and, in contrast to printing problem, one can't just return incorrectly rounded parsing results, such cases are detected instead and slower, but accurate fallback conversion is performed (C Standard Library functions like `strtod()` by default). In this respect **Krosh** is closer to **Grisu3**.
Example
-------
**Printing:**
```cpp
#include <iostream>
#include "floaxie/ftoa.h"
using namespace std;
using namespace floaxie;
int main(int, char**)
{
double pi = 0.1;
char buffer[128];
ftoa(pi, buffer);
cout << "pi: " << pi << ", buffer: " << buffer << endl;
return 0;
}
```
**Parsing:**
```cpp
#include <iostream>
#include "floaxie/atof.h"
using namespace std;
using namespace floaxie;
int main(int, char**)
{
char str[] = "0.1";
char* str_end;
double pi = 0;
pi = atof<double>(str, &str_end);
cout << "pi: " << pi << ", str: " << str << ", str_end: " << str_end << endl;
return 0;
}
```
Building
--------
Building is not required and completely optional, unless you would like to try the examples or tests or build the local documentation. Inside `git` project tree it can be done like this:
```shell
git submodule update --init # to check out common CMake modules' submodule
mkdir build && cd build
cmake -DBUILD_EXAMPLES=1 -DBUILD_TESTS=1 -DBUILD_DOCUMENTATION=1 ../ # switch on the desired flags
cmake --build . # or just `make` on systems with it
```
Adding to the project
---------------------
```shell
git submodule add https://github.com/aclex/floaxie <desired-path-in-your-project>
```
Don't forget to do `git submodule update --init --recursive` out of your project tree to pull submodules properly.
Including to CMake project as a subproject
-------
Since version 1.2 it's possible to include Floaxie as a subproject in any CMake project quite easily thanks to modern CMake `INTERFACE_LIBRARY` target facilities. Unfortunately, this works fully since CMake 3.13, so its minimum required version had to be increased.
`CMakeLists.txt` of a consumer CMake project would look like this (given Floaxie is cloned to `floaxie` subdirectory)':
```cmake
project(foo)
cmake_minimum_required(VERSION 3.13)
# `EXCLUDE_FOR_ALL` here to exclude supplementary targets
# like `install` from the main project target set
add_subdirectory(peli EXCLUDE_FROM_ALL)
add_executable(foo_main foo_main.cpp)
target_link_libraries(foo_main PUBLIC floaxie)
```

View File

@ -0,0 +1,179 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_ATOF_H
#define FLOAXIE_ATOF_H
#include <string>
#include <floaxie/krosh.h>
#include <floaxie/default_fallback.h>
#include <floaxie/conversion_status.h>
/** \brief Floaxie functions templates.
*
* This namespace contains two main public floaxie functions (`atof()` and
* `ftoa()`), as well as several helper functions (e.g. `max_buffer_size()`)
* and internal type and function templates.
*/
namespace floaxie
{
/** \brief Small decorator around returning value to help the client
* optionally receive minor error states along with it.
*
* \tparam FloatType target floating point type to store results.
*/
template<typename FloatType> struct value_and_status
{
/** \brief The returning result value itself. */
FloatType value;
/** \brief Conversion status indicating any problems occurred. */
conversion_status status;
/** \brief Constructs the object with empty value and successful status. */
value_and_status() noexcept : value(), status(conversion_status::success) { }
/** \brief Default conversion operator to `FloatType` to make use of the
* wrapper more transparent. */
operator FloatType() const noexcept { return value; }
};
/** \brief Parses floating point string representation.
*
* Interprets string representation of floating point value using Krosh
* algorithm and, if successful, value of the specified type is returned.
*
* The accepted representation format is ordinary or exponential decimal
* floating point expression, containing:
* - optional sign ('+' or '-')
* - sequence of one or more decimal digits optionally containing decimal
* point character ('.')
* - optional 'e' of 'E' character followed by optional sign ('+' or '-')
* and sequence of one or more decimal digits.
*
* Function doesn't expect any preceding spacing characters and treats the
* representation as incorrect, if there's any.
*
* \tparam FloatType target floating point type to store results.
* \tparam CharType character type (typically `char` or `wchar_t`) the input
* string \p **str** consists of.
* \tparam FallbackCallable fallback conversion function type, in case of
* Krosh is unsure if the result is correctly rounded (default is `strtof()`
* for `float`'s, `strtod()` for `double`'s, `strtold()` for `long double`'s).
*
* \param str buffer containing the string representation of the value.
* \param str_end out parameter, which will contain a pointer to first
* character after the parsed value in the specified buffer. If str_end is
* null, it is ignored.
* \param fallback_func pointer to fallback function. If omitted, by default
* is `strtof()` for `float`'s, `strtod()` for `double`'s, `strtold()` for
* `long double`'s. Null value will lead to undefined behaviour in case of
* algorithm is unsure and fall back to using it.
*
* \return structure containing the parsed value, if the
* input is correct (default constructed value otherwise) and status of the
* conversion made.
*
* \sa `value_and_status`
* \sa `conversion_status`
*/
template
<
typename FloatType,
typename CharType,
typename FallbackCallable = FloatType (const CharType*, CharType**)
>
inline value_and_status<FloatType> atof(const CharType* str, CharType** str_end, FallbackCallable fallback_func = default_fallback<FloatType, CharType>)
{
value_and_status<FloatType> result;
const auto& cr(krosh<FloatType>(str));
if (cr.str_end != str)
{
if (cr.is_accurate)
{
result.value = cr.value;
result.status = cr.status;
}
else
{
result.value = fallback_func(str, str_end);
result.status = check_errno(result.value);
return result;
}
}
if (str_end)
*str_end = const_cast<CharType*>(cr.str_end);
return result;
}
/** \brief Tiny overload for `atof()` function to allow passing `nullptr`
* as `str_end` parameter.
*/
template
<
typename FloatType,
typename CharType,
typename FallbackCallable = FloatType (const CharType*, CharType**)
>
inline value_and_status<FloatType> atof(const CharType* str, std::nullptr_t str_end, FallbackCallable fallback_func = default_fallback<FloatType, CharType>)
{
return atof<FloatType, CharType, FallbackCallable>(str, static_cast<CharType**>(str_end), fallback_func);
}
/** \brief Parses floating point represented in `std::basic_string`.
*
* `atof()` adapter, which may be more useful for cases, where
* `std::basic_string` strings are widely used.
*
* \tparam FloatType target floating point type to store results.
* \tparam CharType character type (typically `char` or `wchar_t`) the input
* string \p **str** consists of.
* \tparam FallbackCallable fallback conversion function type, in case of
* Krosh is unsure if the result is correctly rounded (default is `strtof()`
* for `float`'s, `strtod()` for `double`'s, `strtold()` for `long double`'s).
*
* \param str string representation of the value.
* \param fallback_func pointer to fallback function. If omitted, by default
* is `strtof()` for `float`'s, `strtod()` for `double`'s, `strtold()` for
* `long double`'s. Null value will lead to undefined behaviour in case of
* algorithm is unsure and fall back to using it.
*
* \return structure containing the parsed value, if the
* input is correct (default constructed value otherwise) and status of the
* conversion made.
*
* \sa `value_and_status`
* \sa `conversion_status`
*/
template
<
typename FloatType,
typename CharType,
typename FallbackCallable = FloatType (const CharType*, CharType**)
>
inline value_and_status<FloatType> from_string(const std::basic_string<CharType>& str, FallbackCallable fallback_func = default_fallback<FloatType, CharType>)
{
return atof<FloatType>(str.c_str(), nullptr, fallback_func);
}
}
#endif // FLOAXIE_ATOF_H

View File

@ -0,0 +1,220 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* diy_fp class and helper functions use code and influenced by
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_BIT_OPS_H
#define FLOAXIE_BIT_OPS_H
#include <limits>
#include <algorithm>
#include <cstddef>
#include <cassert>
#include <external/floaxie/floaxie/integer_of_size.h>
namespace floaxie
{
/** \brief Calculates size of type in bits in compile time.
*
* \tparam NumericType type to calculate size in bits of.
*
* \return type size in bits.
*/
template<typename NumericType> constexpr std::size_t bit_size() noexcept
{
return sizeof(NumericType) * std::numeric_limits<unsigned char>::digits;
}
/** \brief Returns a value with bit of the specified power raised.
*
* Calculates a value, which equals to 2 in the specified power, i.e. with
* bit at \p `power` position is `1` and all the remaining bits are `0`.
*
* \tparam NumericType type to store the calculated value.
*
* \param power power (0-based index, counting right to left) of bit to
* raise.
*
* \return value of \p **NumericType** with \p **power**-th bit is `1` and
* all the remaining bits are `0`.
*/
template<typename NumericType> constexpr NumericType raised_bit(std::size_t power)
{
assert(power < bit_size<NumericType>());
return NumericType(1) << power;
}
/** \brief Returns Most Significant Bit (MSB) value for the specified type.
*
* Calculates a value, which is equal to the value of Most
* Significant Bit of the integer type, which has the same length, as the
* specified one. The left most bit of the calculated value is equal to
* `1`, and the remaining bits are `0`.
*
* \tparam FloatType type to calculate MSB value for.
* \tparam NumericType integer type of the same size, as \p **FloatType**.
*
* \return value of Most Significant Bit (MSB).
*/
template<typename FloatType,
typename NumericType = typename integer_of_size<sizeof(FloatType)>::type>
constexpr NumericType msb_value() noexcept
{
return raised_bit<NumericType>(bit_size<NumericType>() - 1);
}
/** \brief Returns maximum unsigned integer value for the specified type.
*
* Calculates maximum value (using `std::numeric_limits`) of the integer
* type, which has the same length, as the specified one. Thus, all bits
* of the calculated value are equal to `1`.
*
* \tparam FloatType type to calculate MSB value for.
* \tparam NumericType integer type of the same size, as \p **FloatType**.
*
* \return maximum value of size the same as of the specified type.
*/
template<typename FloatType,
typename NumericType = typename integer_of_size<sizeof(FloatType)>::type>
constexpr NumericType max_integer_value() noexcept
{
return std::numeric_limits<NumericType>::max();
}
/** \brief Masks `n`-th bit of the specified value.
*
* Calculates a mask standing for the `n`-th bit, performs bitwise **AND**
* operation and returns the value of it.
*
* \param value the value, of which the specified bit is returned.
* \param power power (0-based right-to-left index) of bit to return.
*
* \return integer value, which has \p `power`-th bit of the \p **value**
* and the remaining bits equal to `0`.
*
*/
template<typename NumericType> constexpr bool nth_bit(NumericType value, std::size_t power) noexcept
{
return value & raised_bit<NumericType>(power);
}
/** \brief Returns Most Significant Bit (MSB) of the specified value.
*
* Masks the left most bit of the given value, performs bitwise **AND**
* operation with the mask and the value and returns the result.
*
* \tparam NumericType type of the value.
*
* \param value value to get the highest bit of.
*
* \return integer value, which left most bit of the \p **value** and the
* remaining bits equal to `0`.
*/
template<typename NumericType> constexpr bool highest_bit(NumericType value) noexcept
{
return nth_bit(value, bit_size<NumericType>() - 1);
}
/** \brief Returns mask of \p **n** bits from the right.
*
* \tparam NumericType type of the returned value.
* \param n number of bits from the right to mask.
*
* \return integer value with \p **n** right bits equal to `1` and the
* remaining bits equal to `0`.
*/
template<typename NumericType> constexpr NumericType mask(std::size_t n) noexcept
{
static_assert(!std::is_signed<NumericType>::value, "Unsigned integral type is expected.");
return n < bit_size<NumericType>() ? raised_bit<NumericType>(n) - 1 : std::numeric_limits<NumericType>::max();
}
/** \brief Rectified linear function.
*
* Returns the argument (\p **value**), if it's positive and `0` otherwise.
*
* \param value the argument.
*
* \return \p **value**, if \p **value** > `0`, `0` otherwise.
*
*/
template<typename NumericType> constexpr typename std::make_unsigned<NumericType>::type positive_part(NumericType value) noexcept
{
return static_cast<typename std::make_unsigned<NumericType>::type>((std::max)(0, value));
}
/** \brief Return structure for `round_up` function. */
struct round_result
{
/** \brief Round up result — flag indicating if the value should be
*rounded up (i.e. incremented).
*/
bool value;
/** \brief Flag indicating if the rounding was accurate. */
bool is_accurate;
};
/** \brief Detects if rounding up should be done.
*
* Applies IEEE algorithm of rounding up detection. The rounding criteria
* requires, that rounding bit (the bit to the right of target position,
* which rounding is being performed to) equals to `1`, and one of the
* following conditions is true: * - at least one bit to the right of the rounding bit equals to `1`
* - the bit in the target position equals to `1`
*
* \tparam NumericType type of \p **last_bits** parameter (auto-calculated).
*
* \param last_bits right suffix of the value, where rounding takes place.
* \param round_to_power the power (0-based right-to-left index) of the
* target position (which rounding is being performed to). According to the
* algorithm math it should be greater, than zero, otherwise behaviour is
* undefined.
*
* \returns `round_result` structure with the rounding decision.
*/
template<typename NumericType> inline round_result round_up(NumericType last_bits, std::size_t round_to_power) noexcept
{
round_result ret;
assert(round_to_power > 0);
const NumericType round_bit(raised_bit<NumericType>(round_to_power - 1));
const NumericType check_mask(mask<NumericType>(round_to_power + 1) ^ round_bit);
ret.is_accurate = (last_bits & mask<NumericType>(round_to_power)) != round_bit;
ret.value = (last_bits & round_bit) && (last_bits & check_mask);
return ret;
}
/** \brief `constexpr` version of `std::abs`, as the latter lacks `constepxr`.
*
* And is really not `constexpr` in e.g. Clang.
*
* \returns absolute value of the specified value.
*/
template<typename NumericType> constexpr NumericType constexpr_abs(NumericType value)
{
return NumericType(0) < value ? value : -value;
}
}
#endif // FLOAXIE_BIT_OPS_H

View File

@ -0,0 +1,53 @@
/*
* Copyright 2015-2017 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* cached power literals and cached_power() function use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_CACHED_POWER_H
#define FLOAXIE_CACHED_POWER_H
#include <cstddef>
#include <cassert>
#include <external/floaxie/floaxie/powers_ten_single.h>
#include <external/floaxie/floaxie/powers_ten_double.h>
#include <external/floaxie/floaxie/diy_fp.h>
namespace floaxie
{
/** \brief Returns pre-calculated `diy_fp` value of 10 in the specified
* power using pre-calculated and compiled version of binary mantissa
* and exponent.
*
* \tparam FloatType floating point type to call the values for.
*/
template<typename FloatType> inline diy_fp<FloatType> cached_power(int k) noexcept
{
assert(k >= -static_cast<int>(powers_ten<FloatType>::pow_0_offset));
const std::size_t index = powers_ten<FloatType>::pow_0_offset + k;
return diy_fp<FloatType>(powers_ten<FloatType>::f[index], powers_ten<FloatType>::e[index]);
}
}
#endif // FLOAXIE_CACHED_POWER_H

View File

@ -0,0 +1,34 @@
/*
* Copyright 2015, 2016, 2017 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_CONVERSION_STATUS_H
#define FLOAXIE_CONVERSION_STATUS_H
namespace floaxie
{
/** \brief Enumeration of possible conversion results, either successful or
* not.
*/
enum class conversion_status : unsigned char
{
success, /**< The conversion was successful. */
underflow, /**< An underflow occurred during the conversion. */
overflow /**< An overflow occurred during the conversion. */
};
}
#endif // FLOAXIE_CONVERSION_STATUS_H

View File

@ -0,0 +1,94 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_DEFAULT_FALLBACK_H
#define FLOAXIE_DEFAULT_FALLBACK_H
#include <cstdlib>
#include <cwchar>
#include <floaxie/conversion_status.h>
namespace floaxie
{
/** \brief Function template to wrap C Standard Library floating point
* parse function based on floating-point type and character type (normal
* or wide).
*
* \tparam FloatType floating point type to parse.
* \tparam CharType character type of string to parse.
*/
template<typename FloatType, typename CharType> FloatType default_fallback(const CharType* str, CharType** str_end);
/** \brief `float` and `char`. */
template<> inline float default_fallback<float, char>(const char* str, char** str_end)
{
return std::strtof(str, str_end);
}
/** \brief `double` and `char`. */
template<> inline double default_fallback<double, char>(const char* str, char** str_end)
{
return std::strtod(str, str_end);
}
/** \brief `long double` and `char`. */
template<> inline long double default_fallback<long double, char>(const char* str, char** str_end)
{
return std::strtold(str, str_end);
}
/** \brief `float` and `wchar_t`. */
template<> inline float default_fallback<float, wchar_t>(const wchar_t* str, wchar_t** str_end)
{
return std::wcstof(str, str_end);
}
/** \brief `double` and `wchar_t`. */
template<> inline double default_fallback<double, wchar_t>(const wchar_t* str, wchar_t** str_end)
{
return std::wcstod(str, str_end);
}
/** \brief `long double` and `wchar_t`. */
template<> inline long double default_fallback<long double, wchar_t>(const wchar_t* str, wchar_t** str_end)
{
return std::wcstold(str, str_end);
}
/** \brief Returns `conversion_status` based on `errno` value.
*
* Analyzes current value of `errno` together with the passed conversion
* result and returns `conversion_status` value for the case.
*
* \tparam FloatType floating-point type of the returned value passed.
*
* \p returned_value the value returned after the conversion.
*
* \return status of the last conversion.
*
* \sa `conversion_status`
*/
template<typename FloatType> conversion_status check_errno(FloatType returned_value)
{
if (errno != ERANGE)
return conversion_status::success;
return returned_value ? conversion_status::overflow : conversion_status::underflow;
}
}
#endif // FLOAXIE_DEFAULT_FALLBACK_H

View File

@ -0,0 +1,518 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* diy_fp class and helper functions use code and influenced by
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_DIY_FP_H
#define FLOAXIE_DIY_FP_H
#include <algorithm>
#include <limits>
#include <cstdint>
#include <cassert>
#include <ostream>
#include <utility>
#include <external/floaxie/floaxie/bit_ops.h>
#include <external/floaxie/floaxie/print.h>
#include <external/floaxie/floaxie/type_punning_cast.h>
#include <external/floaxie/floaxie/huge_val.h>
#include <external/floaxie/floaxie/conversion_status.h>
namespace floaxie
{
/** \brief Template structure to define `diy_fp` inner types for
* selected floating point types.
*/
template<typename FloatType> struct diy_fp_traits;
/** \brief `diy_fp_traits` specialization associated with single precision
* floating point type (`float`).
*
* **Mantissa** is stored using the fastest natively supported standard
* integer type almost always it's 32-bit unsigned integer value.
*
* **Exponent** is stored in `int` value. Shorter types aren't used to
* avoid any performance impacts of not using default integer types
* (though it can be effective in terms of cache usage).
*/
template<> struct diy_fp_traits<float>
{
/** \brief Integer type to store mantissa value. */
typedef std::uint32_t mantissa_type;
/** \brief Integer type to store exponent value. */
typedef int exponent_type;
};
/** \brief `diy_fp_traits` specialization associated with double precision
* floating point type (`double`).
*
* **Mantissa** is stored using the biggest natively supported standard
* integer type currently it's 64-bit unsigned integer value. Emulated
* (e.g. *big integer*) integer types are not used, as they don't follow
* the ideas behind the fast integer algorithms (they are slower due to
* the emulation part).
*
* **Exponent** is stored in `int` value.
*
*/
template<> struct diy_fp_traits<double>
{
/** \brief Integer type to store mantissa value. */
typedef std::uint64_t mantissa_type;
/** \brief Integer type to store exponent value. */
typedef int exponent_type;
};
/** \brief Integer representation of floating point value.
*
* The templated type represents floating point value using two integer values, one
* to store **mantissa** and another to hold **exponent**. Concrete types are
* expressed as **diy_fp** specializations, with pre-defined types for **mantissa**
* and **exponent**, suitable to process floating point value of the specified
* precision with maximum efficiency and without losing accuracy.
*
* \tparam FloatType floating point type the representation is instantiated for.
* \tparam Traits some inner settings (mainly, integer types to store mantissa and
* exponent) corresponding to `FloatType`.
*
* The type is used in **Grisu** and **Krosh** algorithms.
*/
template<typename FloatType, typename Traits = diy_fp_traits<FloatType>> class diy_fp
{
public:
/** \brief Mantissa storage type abstraction. */
typedef typename Traits::mantissa_type mantissa_storage_type;
/** \brief Exponent storage type abstraction. */
typedef typename Traits::exponent_type exponent_storage_type;
private:
static_assert(std::numeric_limits<FloatType>::is_iec559, "Only IEEE-754 floating point types are supported");
static_assert(sizeof(FloatType) == sizeof(mantissa_storage_type), "Float type is not compatible with its `diy_fp` representation layout.");
/** \brief Returns value of hidden bit for the specified floating point type.
*
* \return integer value of hidden bit of the specified type in
* `mantissa_storage_type` variable.
*/
static constexpr mantissa_storage_type hidden_bit()
{
return raised_bit<mantissa_storage_type>(std::numeric_limits<FloatType>::digits - 1);
}
public:
/** \brief Default constructor. */
diy_fp() = default;
/** \brief Copy constructor. */
diy_fp(const diy_fp&) = default;
/** \brief Component initialization constructor. */
constexpr diy_fp(mantissa_storage_type mantissa, exponent_storage_type exponent) noexcept : m_f(mantissa), m_e(exponent) { }
/** \brief Initializes `diy_fp` value from the value of floating point
* type.
*
* It splits floating point value into mantissa and exponent
* components, calculates hidden bit of mantissa and initializes
* `diy_fp` value with the results of calculations.
*/
explicit diy_fp(FloatType d) noexcept
{
constexpr auto full_mantissa_bit_size(std::numeric_limits<FloatType>::digits);
constexpr auto mantissa_bit_size(full_mantissa_bit_size - 1); // remember hidden bit
constexpr mantissa_storage_type mantissa_mask(mask<mantissa_storage_type>(mantissa_bit_size));
constexpr mantissa_storage_type exponent_mask((~mantissa_mask) ^ msb_value<FloatType>()); // ignore sign bit
constexpr exponent_storage_type exponent_bias(std::numeric_limits<FloatType>::max_exponent - 1 + mantissa_bit_size);
mantissa_storage_type parts = type_punning_cast<mantissa_storage_type>(d);
m_f = parts & mantissa_mask;
m_e = (parts & exponent_mask) >> mantissa_bit_size;
if (m_e)
{
m_f += hidden_bit();
m_e -= exponent_bias;
}
else
{
m_e = 1 - exponent_bias;
}
}
/** \brief Downsample result structure.
*
*/
struct downsample_result
{
/** \brief Downsampled floating point result. */
FloatType value;
/** \brief Status showing possible under- or overflow found during downsampling. */
conversion_status status;
/** \brief Flag indicating if the conversion is accurate (no
* [rounding errors] (http://www.exploringbinary.com/decimal-to-floating-point-needs-arbitrary-precision/). */
bool is_accurate;
};
/** \brief Convert `diy_fp` value back to floating point type correctly
* downsampling mantissa value.
*
* The caller should ensure, that the current mantissa value is not null
* and the whole `diy_fp` value is normalized, otherwise the behaviour is
* undefined.
*
* \return result structure with floating point value of the specified type.
*/
downsample_result downsample()
{
downsample_result ret;
ret.is_accurate = true;
ret.status = conversion_status::success;
assert(m_f != 0);
assert(is_normalized());
constexpr auto full_mantissa_bit_size(std::numeric_limits<FloatType>::digits);
constexpr auto mantissa_bit_size(full_mantissa_bit_size - 1); // remember hidden bit
constexpr mantissa_storage_type my_mantissa_size(bit_size<mantissa_storage_type>());
constexpr mantissa_storage_type mantissa_mask(mask<mantissa_storage_type>(mantissa_bit_size));
constexpr exponent_storage_type exponent_bias(std::numeric_limits<FloatType>::max_exponent - 1 + mantissa_bit_size);
constexpr std::size_t lsb_pow(my_mantissa_size - full_mantissa_bit_size);
const auto f(m_f);
if (m_e >= std::numeric_limits<FloatType>::max_exponent)
{
ret.value = huge_value<FloatType>();
ret.status = conversion_status::overflow;
return ret;
}
if (m_e + int(my_mantissa_size) < std::numeric_limits<FloatType>::min_exponent - int(mantissa_bit_size))
{
ret.value = FloatType(0);
ret.status = conversion_status::underflow;
return ret;
}
const std::size_t denorm_exp(positive_part(std::numeric_limits<FloatType>::min_exponent - int(mantissa_bit_size) - m_e - 1));
assert(denorm_exp < my_mantissa_size);
const std::size_t shift_amount(std::max(denorm_exp, lsb_pow));
mantissa_storage_type parts = m_e + shift_amount + exponent_bias - (denorm_exp > lsb_pow);
parts <<= mantissa_bit_size;
const auto& round(round_up(f, shift_amount));
parts |= ((f >> shift_amount) + round.value) & mantissa_mask;
ret.value = type_punning_cast<FloatType>(parts);
ret.is_accurate = round.is_accurate;
return ret;
}
/** \brief Mantissa component. */
constexpr mantissa_storage_type mantissa() const
{
return m_f;
}
/** \brief Exponent component. */
constexpr exponent_storage_type exponent() const
{
return m_e;
}
/** \brief Checks if the value is normalized.
*
* The behaviour is undefined, if called for null value.
*
*/
bool is_normalized() const noexcept
{
assert(m_f != 0); // normalization of zero is undefined
return m_f & msb_value<mantissa_storage_type>();
}
/** \brief Normalizes the value the common way.
*
* The caller should ensure, that the current mantissa value is not null,
* otherwise the behaviour is undefined.
*/
void normalize() noexcept
{
assert(m_f != 0); // normalization of zero is undefined
while (!highest_bit(m_f))
{
m_f <<= 1;
m_e--;
}
}
/** \brief Copy assignment operator. */
diy_fp& operator=(const diy_fp&) = default;
/** \brief Subtracts the specified `diy_fp` value from the current.
*
* Simple mantissa subtraction of `diy_fp` values.
*
* If exponents of the values differ or mantissa of left value is less,
* than mantissa of right value, the behaviour is undefined.
*
* \param rhs subtrahend.
*
* \return reference to current value, i.e. the result of the
* subtraction.
*/
diy_fp& operator-=(const diy_fp& rhs) noexcept
{
assert(m_e == rhs.m_e && m_f >= rhs.m_f);
m_f -= rhs.m_f;
return *this;
}
/** \brief Non-destructive version of `diy_fp::operator-=()`. */
diy_fp operator-(const diy_fp& rhs) const noexcept
{
return diy_fp(*this) -= rhs;
}
/** \brief Fast and coarse multiplication.
*
* Performs multiplication of `diy_fp` values ignoring some carriers
* for the sake of performance. This multiplication algorithm is used
* in original **Grisu** implementation and also works fine for
* **Krosh**.
*
* \param rhs multiplier.
*
* \return reference to current value, i.e. the result of the
* multiplication.
*/
diy_fp& operator*=(const diy_fp& rhs) noexcept
{
constexpr std::size_t half_width = bit_size<mantissa_storage_type>() / 2;
constexpr auto mask_half = mask<mantissa_storage_type>(half_width);
const mantissa_storage_type a = m_f >> half_width;
const mantissa_storage_type b = m_f & mask_half;
const mantissa_storage_type c = rhs.m_f >> half_width;
const mantissa_storage_type d = rhs.m_f & mask_half;
const mantissa_storage_type ac = a * c;
const mantissa_storage_type bc = b * c;
const mantissa_storage_type ad = a * d;
const mantissa_storage_type bd = b * d;
const mantissa_storage_type tmp = (bd >> half_width) + (ad & mask_half) + (bc & mask_half) + raised_bit<mantissa_storage_type>(half_width - 1);
m_f = ac + (ad >> half_width) + (bc >> half_width) + (tmp >> half_width);
m_e += rhs.m_e + bit_size<mantissa_storage_type>();
return *this;
}
/** \brief Non-destructive version of `diy_fp::operator*=()`. */
diy_fp operator*(const diy_fp& rhs) const noexcept
{
return diy_fp(*this) *= rhs;
}
/** \brief Increment (prefix) with mantissa overflow control. */
diy_fp& operator++() noexcept
{
if (m_f < std::numeric_limits<diy_fp::mantissa_storage_type>::max())
{
++m_f;
}
else
{
m_f >>= 1;
++m_f;
++m_e;
}
return *this;
}
/** \brief Postfix increment version. */
diy_fp operator++(int) noexcept
{
auto temp = *this;
++(*this);
return temp;
}
/** \brief Decrement (prefix) with mantissa underflow control. */
diy_fp& operator--() noexcept
{
if (m_f > 1)
{
--m_f;
}
else
{
m_f <<= 1;
--m_f;
--m_e;
}
return *this;
}
/** \brief Postfix decrement version. */
diy_fp operator--(int) noexcept
{
auto temp = *this;
--(*this);
return temp;
}
/** \brief Equality of `diy_fp` values.
*
* Just member-wise equality check.
*/
bool operator==(const diy_fp& d) const noexcept
{
return m_f == d.m_f && m_e == d.m_e;
}
/** \brief Inequality of `diy_fp` values.
*
* Negation of `diy_fp::operator==()` for consistency.
*/
bool operator!=(const diy_fp& d) const noexcept
{
return !operator==(d);
}
/** \brief Calculates boundary values (M+ and M-) for the specified
* floating point value.
*
* Helper function for **Grisu2** algorithm, which first converts the
* specified floating point value to `diy_fp` and then calculates lower
* (M-) and higher (M+) boundaries of it and thus of original accurate
* floating point value.
*
* These two boundary values define the range where all the values are
* correctly rounded to the specified floating point value, so any
* value within this range can be treated as correct representation of
* the specified one.
*
* \param d floating point value to calculate boundaries for.
*
* \return `std::pair` of two `diy_fp` values, **M-** and **M+**,
* respectively.
*
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
static std::pair<diy_fp, diy_fp> boundaries(FloatType d) noexcept
{
std::pair<diy_fp, diy_fp> result;
diy_fp &mi(result.first), &pl(result.second);
pl = diy_fp(d);
mi = pl;
pl.m_f <<= 1;
pl.m_f += 1;
pl.m_e -= 1;
pl.normalize_from_ieee754(); // as we increase precision of IEEE-754 type by 1
const unsigned char shift_amount(1 + (mi.m_f == hidden_bit()));
mi.m_f <<= shift_amount;
mi.m_f -= 1;
mi.m_e -= shift_amount;
mi.m_f <<= mi.m_e - pl.m_e;
mi.m_e = pl.m_e;
return result;
}
/** \brief Prints `diy_fp` value.
*
* \param os `std::basic_ostream` to print to.
* \param v `diy_fp` value to print.
*
* \return `std::basic_ostream` with the \p **v** value printed.
*/
template<typename Ch, typename Alloc>
friend std::basic_ostream<Ch, Alloc>& operator<<(std::basic_ostream<Ch, Alloc>& os, const diy_fp& v)
{
os << "(f = " << print_binary(v.m_f) << ", e = " << v.m_e << ')';
return os;
}
private:
/** \brief Normalizes the value using additional information on
* mantissa content of the `FloatType`.
* Mantissa value is treated as of the width defined in
* `std::numeric_limits`. This information speeds up the normalization,
* allowing to shift the value by several positions right at one take,
* rather than shifting it by one step and checking if it's still not normalized.
*
* The caller should ensure, that the current mantissa value is not null
* and is really represented in IEEE-754 format, otherwise the behaviour
* is undefined.
*/
void normalize_from_ieee754() noexcept
{
constexpr auto mantissa_bit_width(std::numeric_limits<FloatType>::digits);
static_assert(mantissa_bit_width >= 0, "Mantissa bit width should be positive.");
assert(m_f != 0); // normalization of zero is undefined
while (!nth_bit(m_f, mantissa_bit_width))
{
m_f <<= 1;
m_e--;
}
constexpr mantissa_storage_type my_mantissa_size(bit_size<mantissa_storage_type>());
constexpr mantissa_storage_type e_diff = my_mantissa_size - mantissa_bit_width - 1;
m_f <<= e_diff;
m_e -= e_diff;
}
mantissa_storage_type m_f;
exponent_storage_type m_e;
};
}
#endif // FLOAXIE_DIY_FP_H

View File

@ -0,0 +1,142 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_FRACTION_H
#define FLOAXIE_FRACTION_H
#include <cstddef>
#include <floaxie/static_pow.h>
namespace floaxie
{
/** \brief Recursive structure template used to convert decimal common
* fraction to binary common fraction.
*
* The structure template hierarchy is used to calculate the binary common
* fraction value, which is approximately equal to given decimal common
* fraction.
*
* Example:
* ~~~
* 0.625 = 0.101
* ~~~
*
* \tparam T numeric type of input value and result.
* \tparam decimal_digits number of significant decimal digits in
* numerator (equals to the power of ten in denominator); for example, for
* 0.625 \p **decimal_digits** is 3.
* \tparam binary_digits number of significant binary digits in numerator
* of the result, which is defined by the necessary accuracy of
* result approximation; for example, for 0.101 \p **decimal_digits** is 3.
* \tparam current_binary_digit template cursor used to implement
* recursive descent.
* \tparam terminal automatically calculated flag of recursion termination.
*/
template
<
typename T,
std::size_t decimal_digits,
std::size_t binary_digits,
std::size_t current_binary_digit,
bool terminal = (binary_digits == current_binary_digit)
>
struct fraction_converter;
/** \brief Intermediate step implementation of `fraction_converter`
* template.
*/
template
<
typename T,
std::size_t decimal_digits,
std::size_t binary_digits,
std::size_t current_binary_digit
>
struct fraction_converter<T, decimal_digits, binary_digits, current_binary_digit, false>
{
/** \brief Calculates \p `current_binary_digit`-th digit of the result.
*
* \param decimal_numerator value of decimal numerator of common
* fraction to convert.
*
* \return properly shifted value of \p `current_binary_digit`-th
* digit of the result.
*/
static T convert(T decimal_numerator)
{
constexpr T numerator(static_pow<10, decimal_digits>());
constexpr T denominator(static_pow<2, current_binary_digit>());
constexpr T decimal_fraction(numerator / denominator);
constexpr std::size_t shift_amount(binary_digits - current_binary_digit);
const T decision(decimal_numerator >= decimal_fraction);
const T residue(decimal_numerator - decision * decimal_fraction);
return (decision << shift_amount) |
fraction_converter<T, decimal_digits, binary_digits, current_binary_digit + 1>::convert(residue);
}
};
/** \brief Terminal step implementation of `fraction_converter` template.
*
*/
template
<
typename T,
std::size_t decimal_digits,
std::size_t binary_digits,
std::size_t current_binary_digit
>
struct fraction_converter<T, decimal_digits, binary_digits, current_binary_digit, true>
{
/** \brief Calculates least significant digit of the result.
*
* \param decimal_numerator value of decimal numerator of common
* fraction to convert.
*
* \return right most (least significant) digit of the result.
*/
static T convert(T decimal_numerator)
{
constexpr T numerator(static_pow<10, decimal_digits>());
constexpr T denominator(static_pow<2, current_binary_digit>());
constexpr T decimal_fraction(numerator / denominator);
return decimal_numerator >= decimal_fraction;
}
};
/** \brief Wrapper function to convert numerator of decimal common fraction
* to numerator of approximately equal binary common fraction with the
* specified accuracy.
*
* \tparam decimal_digits number of digits in decimal numerator to observe
* (input accuracy).
* \tparam binary_digits number of digits in binary numerator to generate
* (output accuracy).
*
* \return value of binary numerator with the specified accuracy as
* calculated by `fraction_converter`.
*/
template<std::size_t decimal_digits, std::size_t binary_digits, typename T> inline T convert_numerator(T decimal_numerator)
{
return fraction_converter<T, decimal_digits, binary_digits, 1>::convert(decimal_numerator);
}
}
#endif // FLOAXIE_FRACTION_H

View File

@ -0,0 +1,202 @@
/*
* Copyright 2015-2022 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_FTOA_H
#define FLOAXIE_FTOA_H
#include <string>
#include <type_traits>
#include <cmath>
#include <cstddef>
#include <cassert>
#include <external/floaxie/floaxie/grisu.h>
#include <external/floaxie/floaxie/prettify.h>
namespace floaxie
{
/** \brief Returns maximum size of buffer can ever be required by `ftoa()`.
*
* Maximum size of buffer passed to `ftoa()` guaranteed not to lead to
* undefined behaviour.
*
* \tparam FloatType floating point type, which value is planned to be
* printed to the buffer.
*
* \return maximum size of buffer, which can ever be used in the very worst
* case.
*/
template<typename ValueType> constexpr std::size_t max_buffer_size() noexcept
{
typedef typename std::decay<ValueType>::type FloatType;
// digits, '.' (or 'e' plus three-digit power with optional sign) and '\0'
return max_digits<FloatType>() + 1 + 1 + 3 + 1;
}
/** \brief Prints floating point value to optimal string representation.
*
* The function prints the string representation of the specified floating
* point value using
* [**Grisu2**](http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
* algorithm and tries to get it as shorter, as possible. Usually it
* succeeds, but sometimes fails, and the output representation is not
* the shortest for this value. For the sake of speed improvement this is
* ignored, while there's **Grisu3** algorithm which rules this out
* informing the caller of the failure, so that it can call slower, but
* more accurate algorithm in this case.
*
* The format of the string representation is one of the following:
* 1. Decimal notation, which contains:
* - minus sign ('-') in case of negative value
* - sequence of one or more decimal digits optionally containing
* decimal point character ('.')
* 2. Decimal exponent notation, which contains:
* - minus ('-') sign in case of negative value
* - sequence of one or more decimal digits optionally containing
* decimal point character ('.')
* - 'e' character followed by minus sign ('-') in case of negative
* power of the value (i.e. the specified value is < 1) and
* sequence of one, two of three decimal digits.
*
* \tparam FloatType type of floating point value, calculated using passed
* input parameter \p **v**.
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param v floating point value to print.
* \param buffer character buffer of enough size (see `max_buffer_size()`)
* to print the representation to.
*
* \return number of characters actually written.
*
* \see `max_buffer_size()`
*/
template<typename FloatType, typename CharType> inline std::size_t ftoa(FloatType v, CharType* buffer) noexcept
{
if (std::isnan(v))
{
buffer[0] = 'n';
buffer[1] = 'a';
buffer[2] = 'n';
buffer[3] = '\0';
return 3;
}
else if (std::isinf(v))
{
if (v > 0)
{
buffer[0] = 'i';
buffer[1] = 'n';
buffer[2] = 'f';
buffer[3] = '\0';
return 3;
}
else
{
buffer[0] = '-';
buffer[1] = 'i';
buffer[2] = 'n';
buffer[3] = 'f';
buffer[4] = '\0';
return 4;
}
}
else if (v == 0)
{
buffer[0] = '0';
buffer[1] = '\0';
return 1;
}
else
{
*buffer = '-';
buffer += v < 0;
constexpr int alpha(grisu_parameters<FloatType>.alpha), gamma(grisu_parameters<FloatType>.gamma);
constexpr unsigned int decimal_scientific_threshold(16);
int len, K;
grisu2<alpha, gamma>(v, buffer, &len, &K);
return (v < 0) + prettify<decimal_scientific_threshold>(buffer, len, K);
}
}
/** \brief Prints floating point value to optimal representation in
* `std::basic_string`.
*
* Wrapper function around `ftoa()`, which returns `std::basic_string`,
* rather than writing to the specified character buffer. This may be
* more useful, if working with `std::basic_string` strings is preferred.
* Please note, however, than common usage scenarios might be significantly
* slower, when many `std::basic_string`'s are created with this function
* and concatenated to each other, than when the outputs of `ftoa()` calls
* are written to one long buffer.
*
* \tparam FloatType type of floating point value, calculated using passed
* input parameter \p **v**.
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param v floating point value to print.
* \param buffer character buffer of enough size (see `max_buffer_size()`)
* to print the representation to.
*
* \see `ftoa()`
*/
template<typename FloatType, typename CharType> inline std::basic_string<CharType> to_basic_string(FloatType v)
{
std::basic_string<CharType> result(max_buffer_size<FloatType>(), CharType());
ftoa(v, &result.front());
result.resize(std::char_traits<CharType>::length(result.data()));
result.shrink_to_fit();
return result;
}
/** \brief 'Specialization' of `to_basic_string()` template for `std::string`. */
template<typename FloatType> inline std::string to_string(FloatType v)
{
return to_basic_string<FloatType, char>(v);
}
/** \brief 'Specialization' of `to_basic_string()` template for `std::wstring`. */
template<typename FloatType> inline std::wstring to_wstring(FloatType v)
{
return to_basic_string<FloatType, wchar_t>(v);
}
/** \brief 'Specialization' of `to_basic_string()` template for `std::u16string`. */
template<typename FloatType> inline std::u16string to_u16string(FloatType v)
{
return to_basic_string<FloatType, char16_t>(v);
}
/** \brief 'Specialization' of `to_basic_string()` template for `std::u32string`. */
template<typename FloatType> inline std::u32string to_u32string(FloatType v)
{
return to_basic_string<FloatType, char32_t>(v);
}
}
#endif // FLOAXIE_FTOA_H

View File

@ -0,0 +1,303 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* digin_gen(), grisu() functions and kappa precalculation approach
* were greatly influenced by
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz),
* "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
* and Milo Yip's Grisu implementation
* (https://github.com/miloyip/dtoa-benchmark)
*/
#ifndef FLOAXIE_GRISU_H
#define FLOAXIE_GRISU_H
#include <utility>
#include <limits>
#include <cstring>
#include <cstddef>
#include <cassert>
#include <external/floaxie/floaxie/diy_fp.h>
#include <external/floaxie/floaxie/cached_power.h>
#include <external/floaxie/floaxie/k_comp.h>
#include <external/floaxie/floaxie/static_pow.h>
#include <external/floaxie/floaxie/integer_of_size.h>
#include <external/floaxie/floaxie/bit_ops.h>
#include <external/floaxie/floaxie/memwrap.h>
namespace floaxie
{
/** \brief Half of `diy_fp::mantissa_storage_type` to use in calculations.
*/
typedef std::uint32_t half_of_mantissa_storage_type;
/** \brief `std::pair` containing exponent value and the corresponding
* power of 10.
*/
typedef std::pair<unsigned char, half_of_mantissa_storage_type> kappa_pair_type;
/** \brief Bind exponent value and the corresponding power of 10. */
template<unsigned char pow> constexpr kappa_pair_type make_kappa_div()
{
return kappa_pair_type(pow, static_pow<10, pow - 1>());
}
/** \brief Ground specialization of `make_kappa_div()` template. */
template<> constexpr kappa_pair_type make_kappa_div<0>()
{
return kappa_pair_type(0, 1);
}
/** \brief Calculate the biggest power of 10 divisor for the specified
* value.
*/
constexpr kappa_pair_type calculate_kappa_div(half_of_mantissa_storage_type n) noexcept
{
if (n < static_pow<10, 1>()) return make_kappa_div<1>();
if (n < static_pow<10, 2>()) return make_kappa_div<2>();
if (n < static_pow<10, 3>()) return make_kappa_div<3>();
if (n < static_pow<10, 4>()) return make_kappa_div<4>();
if (n < static_pow<10, 5>()) return make_kappa_div<5>();
if (n < static_pow<10, 6>()) return make_kappa_div<6>();
if (n < static_pow<10, 7>()) return make_kappa_div<7>();
if (n < static_pow<10, 8>()) return make_kappa_div<8>();
if (n < static_pow<10, 9>()) return make_kappa_div<9>();
return make_kappa_div<10>();
}
/** \brief Defines the maximum possible number of digits to be generated by
* the algorithm.
*
* \tparam FloatType floating point type to calculate the maximum number for.
*/
template<typename FloatType> constexpr std::size_t max_digits() noexcept
{
return std::numeric_limits<half_of_mantissa_storage_type>::digits10 +
std::numeric_limits<typename diy_fp<FloatType>::mantissa_storage_type>::digits10;
}
template<bool positive_exponent> struct digit_gen_select;
/** \brief Template class to select `digit_gen` implementation avoiding
* function template partial specialization.
*/
template<> struct digit_gen_select<false>
{
/** \brief Function of digit generation for the case of α < 0 and γ < 0.
*
* This is probably the fastest `digit_gen()` algorithm, which implies,
* that both **M+** and **M-** exponents are not positive.
*
* Implemented as static member to solve the problem of partial specialization
* of `digit_gen<bool, FloatType, CharType>()`.
*
* \tparam FloatType floating point type of `diy_fp` (`float` for single precision,
* `double` for double precision) of \p **Mm** and \p **Mp**.
* \tparam CharType character type (typically `char` or `wchar_t`) of \p **buffer**.
*
* \param buffer - buffer where digits are generated to.
* \param len - output parameter to pass the length of the digit sequence written.
* \param K - input/output parameter to reflect K modifications made.
*
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
template<typename FloatType, typename CharType>
inline static void gen(const diy_fp<FloatType>& Mp, const diy_fp<FloatType>& Mm, CharType* buffer, int* len, int* K) noexcept
{
assert(Mp.exponent() <= 0);
const diy_fp<FloatType>& delta(Mp - Mm);
const diy_fp<FloatType> one(raised_bit<typename diy_fp<FloatType>::mantissa_storage_type>(-Mp.exponent()), Mp.exponent());
half_of_mantissa_storage_type p1 = Mp.mantissa() >> -one.exponent();
typename diy_fp<FloatType>::mantissa_storage_type p2 = Mp.mantissa() & (one.mantissa() - 1);
assert(p1 || p2);
*len = 0;
auto delta_f = delta.mantissa();
const bool close_to_delta = p2 <= delta_f;
if (p1)
{
auto&& kappa_div(calculate_kappa_div(p1));
unsigned char& kappa(kappa_div.first);
half_of_mantissa_storage_type& div(kappa_div.second);
while (kappa > 0 && p1)
{
buffer[(*len)++] = '0' + p1 / div;
p1 %= div;
kappa--;
div /= 10;
}
if (close_to_delta)
{
*K += kappa;
return;
}
else
{
wrap::memset(buffer + (*len), CharType('0'), kappa);
(*len) += kappa;
}
}
const bool some_already_written = (*len) > 0;
unsigned char kappa(0);
while (p2 > delta_f)
{
p2 *= 10;
const unsigned char d = p2 >> -one.exponent();
if (some_already_written || d)
buffer[(*len)++] = '0' + d;
p2 &= one.mantissa() - 1;
++kappa;
delta_f *= 10;
}
*K -= kappa;
}
};
/** \brief Digit generation function template.
*
* Digit generation algorithm tries to find the value in the range of
* **(M-, M+)** with the shortest possible string representation, which is
* then treated as the representation of the original value, since it
* converts back to it.
*
* General (slow) implementation is of little use, but the optimized
* versions of the function relies so much on α and γ values used in
* **Grisu** algorithm. This implementation requires both α and γ be
* greater of equal, than the bit size of `diy_fp::mantissa_storage_type`
* to use the most optimal `digit_gen()` implementation.
*
* \tparam alpha α value of **Grisu** algorithm.
* \tparam gamma γ value of **Grisu** algorithm.
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param Mp **M+** value (right boundary).
* \param Mm **M-** value (left boundary).
* \param buffer character buffer to print the representation to
* \param len output parameter to return the length of the representation
* printed.
* \param K output parameter to return **K** (decimal exponent) of the
* value.
*
* \see `max_digits()`
* \see `max_buffer_size()`
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
template<int alpha, int gamma,
typename FloatType, typename CharType>
inline void digit_gen(const diy_fp<FloatType>& Mp, const diy_fp<FloatType>& Mm, CharType* buffer, int* len, int* K) noexcept
{
static_assert(static_cast<std::size_t>(constexpr_abs(alpha)) >= bit_size<typename diy_fp<FloatType>::mantissa_storage_type>() / 2 &&
static_cast<std::size_t>(constexpr_abs(gamma)) >= bit_size<typename diy_fp<FloatType>::mantissa_storage_type>() / 2,
"Current implementation supports only α and γ, which absolute values are equal or higher, "
"than a half of integer mantissa bit size (typically 32) for performance reasons.");
constexpr bool exponent_is_positive = alpha > 0 && gamma > 0;
digit_gen_select<exponent_is_positive>::gen(Mp, Mm, buffer, len, K);
}
/** \brief **Grisu2** algorithm implementation.
*
* \tparam alpha α value of **Grisu** algorithm.
* \tparam gamma γ value of **Grisu** algorithm.
* \tparam FloatType type of input floating-point value (calculated by type
* of \p **v** parameter).
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param v floating point value to print.
* \param buffer large enough character buffer to print to.
* \param length output parameter to return the length of printed
* representation.
* \param K output parameter to return **K** (decimal exponent) of the
* value.
*
* \see `max_digits()`
* \see `max_buffer_size()`
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
template<int alpha, int gamma,
typename FloatType, typename CharType> inline void grisu2(FloatType v, CharType* buffer, int* length, int* K) noexcept
{
static_assert(alpha <= gamma - 3,
"It's imposed that γα + 3, since otherwise it's not always possible to find a proper decimal cached power");
std::pair<diy_fp<FloatType>, diy_fp<FloatType>>&& w(diy_fp<FloatType>::boundaries(v));
diy_fp<FloatType> &w_m(w.first), &w_p(w.second);
const int mk = k_comp_exp<alpha, gamma>(w_p.exponent());
const diy_fp<FloatType>& c_mk(cached_power<FloatType>(mk));
w_m *= c_mk;
w_p *= c_mk;
++w_m;
--w_p;
*K = -mk;
digit_gen<alpha, gamma>(w_p, w_m, buffer, length, K);
}
/** \brief Structure to hold Grisu algorithm parameters, **α** and **γ**. */
struct parameters
{
int alpha, gamma;
};
/** \brief Template variable of Grisu algorithm parameters.
*
* Used to provide proper values of Grisu algorithm for the specified
* floating point type.
*/
template<typename FloatType> constexpr parameters grisu_parameters
{
-(int(bit_size<FloatType>() / 2 + 3)), // α
-int(bit_size<FloatType>() / 2) // γ
};
}
#endif // FLOAXIE_GRISU_H

View File

@ -0,0 +1,57 @@
/*
* Copyright 2015, 2016, 2017 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_HUGE_VAL_H
#define FLOAXIE_HUGE_VAL_H
#include <limits>
#include <cmath>
namespace floaxie
{
/** \brief Variable template to unify getting `HUGE_VAL` for
* different floating point types in parameterized manner.
*
* \tparam FloatType floating point type to get `HUGE_VAL` for.
*
* \see [HUGE_VALF, HUGE_VAL, HUGE_VALL]
* (http://en.cppreference.com/w/cpp/numeric/math/HUGE_VAL)
*/
template<typename FloatType> constexpr inline FloatType huge_value() noexcept
{
return std::numeric_limits<FloatType>::infinity();
}
/** \brief `float`. */
template<> constexpr inline float huge_value<float>() noexcept
{
return HUGE_VALF;
}
/** \brief `double`. */
template<> constexpr inline double huge_value<double>() noexcept
{
return HUGE_VAL;
}
/** \brief `long double`. */
template<> constexpr inline long double huge_value<long double>() noexcept
{
return HUGE_VALL;
}
}
#endif // FLOAXIE_HUGE_VAL_H

View File

@ -0,0 +1,51 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_INTEGER_OF_SIZE_H
#define FLOAXIE_INTEGER_OF_SIZE_H
#include <cstdint>
#include <cstddef>
namespace floaxie
{
/** \brief Identity type — hold the specified type in internal `typedef`.
*
* \tparam T type to hold.
*/
template<typename T> struct identity
{
/** \brief Held type. */
typedef T type;
};
/** \brief Maps some of unsigned integer types to their sizes.
*
* Useful for choosing unsigned integer type of the same width as some
* target type (e.g. floating point) to increase possible accuracy.
*
* \tparam size size in bytes of the desired type.
*/
template<std::size_t size> struct integer_of_size : identity<std::uintmax_t()> {};
/** \brief Specialization for 64-bit unsigned integer. */
template<> struct integer_of_size<sizeof(std::uint64_t)> : identity<std::uint64_t> {};
/** \brief Specialization for 32-bit unsigned integer. */
template<> struct integer_of_size<sizeof(std::uint32_t)> : identity<std::uint32_t> {};
}
#endif // FLOAXIE_INTEGER_OF_SIZE_H

View File

@ -0,0 +1,54 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* k_comp() function uses code taken from
* Florian Loitsch's "Printing Floating-Point Numbers Quickly
* and Accurately with Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_K_COMP_H
#define FLOAXIE_K_COMP_H
namespace floaxie
{
/** \brief Compiled value of \f$log{10} 2 \f$ */
constexpr auto lg_2(0.30102999566398114); // 1 / log2(10) = lg(2) ≈ 0.30102999566398114
/** \brief Calculate **K** decimal exponent value by binary exponent.
*
* We ignore mantissa component (q) in exponent to eliminate
* excessive add and subtract of it during K computation.
*
* Function name was changed to not confuse it with the original
* k_comp() function from reference paper where this component
* is considered.
*
* \tparam alpha α value of **Grisu** algorithm.
* \tparam gamma γ value of **Grisu** algorithm.
*
* \param e binary exponent of the floating point value.
*
* \see [Printing Floating-Point Numbers Quickly and Accurately with
* Integers]
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
template<int alpha, int gamma> constexpr int k_comp_exp(int e)
{
return (alpha - e - 1) * lg_2 + (e + 1 < alpha);
}
}
#endif // FLOAXIE_K_COMP_H

View File

@ -0,0 +1,621 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_CROSH_H
#define FLOAXIE_CROSH_H
#include <vector>
#include <locale>
#include <cstddef>
#include <cmath>
#include <cassert>
#include <floaxie/diy_fp.h>
#include <floaxie/static_pow.h>
#include <floaxie/k_comp.h>
#include <floaxie/cached_power.h>
#include <floaxie/bit_ops.h>
#include <floaxie/fraction.h>
#include <floaxie/conversion_status.h>
namespace floaxie
{
/** \brief Maximum number of decimal digits mantissa of `diy_fp` can hold. */
template<typename FloatType> constexpr std::size_t decimal_q = std::numeric_limits<typename diy_fp<FloatType>::mantissa_storage_type>::digits10;
/** \brief Maximum number of necessary binary digits of fraction part. */
constexpr std::size_t fraction_binary_digits(7);
/** \brief Maximum number of decimal digits of fraction part, which can be observed. */
constexpr std::size_t fraction_decimal_digits(4);
/** \brief Maximum length of input string (2 KB). */
constexpr std::size_t maximum_offset = 2048;
/** \brief Maximum number of decimal digits in the exponent value. */
constexpr std::size_t exponent_decimal_digits(3);
/** \brief Tries to find and eat NaN representation in one of two forms.
*
* Searches for either "NAN" or "NAN(<character sequence>)" form of NaN
* (not a number) value representation (case insensitive) to help
* converting it to quiet NaN and finding the end of the read value.
*
* \param str character buffer to analyze.
*
* \return number of consumed characters. Naturally, if it's equal to zero,
* NaN representation wasn't found.
*/
template<typename CharType> std::size_t eat_nan(const CharType* str) noexcept
{
std::size_t eaten(0);
if ((str[0] == 'a' || str[0] == 'A') && (str[1] == 'n' || str[1] == 'N'))
{
const CharType* cp = str + 2;
eaten = 2;
/* Match `(n-char-sequence-digit)'. */
if (*cp == '(')
{
do
++cp;
while ((*cp >= '0' && *cp <= '9') ||
(std::tolower(*cp, std::locale()) >= 'a' && std::tolower(*cp, std::locale()) <= 'z') ||
*cp == '_');
if (*cp == ')')
eaten = cp - str + 1;
}
}
return eaten;
}
/** \brief Tries to find and eat infinity representation.
*
* Searches for either "inf" or "infinity" sequence (case insensitive)
* to determine infinite floating point value representation.
*
* \param str character buffer to analyze.
*
* \return number of consumed characters. Naturally, if it's equal to zero,
* infinity representation wasn't found.
*/
template<typename CharType> std::size_t eat_inf(const CharType* str) noexcept
{
std::size_t eaten(0);
if ((str[0] == 'n' || str[0] == 'N') && (str[1] == 'f' || str[1] == 'F'))
{
const CharType* cp = str + 2;
eaten = 2;
if (*cp == 'i' || *cp == 'I')
{
++cp;
const std::array<CharType, 4> suffix {{ 'n', 'i', 't', 'y' }};
auto it = suffix.cbegin();
while (it != suffix.cend() && std::tolower(*cp, std::locale()) == *it)
{
++cp;
++it;
}
if (it == suffix.cend())
eaten = cp - str;
}
}
return eaten;
}
/** \brief Extracts up to \p **kappa** decimal digits from fraction part.
*
* Extracts decimal digits from fraction part and returns it as numerator
* value with denominator equal to \f$10^{\kappa}\f$.
*
* \tparam kappa maximum number of decimal digits to extract.
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) \p **str**
* consists of.
*
* \param str character buffer to extract from.
*
* \return Numerator value of the extracted decimal digits (i.e. as they
* are actually written after the decimal point).
*/
template<std::size_t kappa, typename CharType>
inline unsigned int extract_fraction_digits(const CharType* str)
{
static_assert(kappa <= std::numeric_limits<int>::digits10, "Extracting values, exceeding 'int' capacity, is not supported.");
std::array<unsigned char, kappa> parsed_digits;
parsed_digits.fill(0);
for (std::size_t pos = 0; pos < kappa; ++pos)
{
const auto c = str[pos] - '0';
if (c >= 0 && c <= 9)
parsed_digits[pos] = c;
else
break;
}
unsigned int result(0);
std::size_t pow(0);
for (auto rit = parsed_digits.rbegin(); rit != parsed_digits.rend(); ++rit)
result += (*rit) * seq_pow<unsigned int, 10, kappa>(pow++);
return result;
}
/** \brief Type of special value. */
enum class speciality : unsigned char
{
/** \brief Normal value - no special. */
no = 0,
/** \brief NaN (not a number) value. */
nan,
/** \brief infinity value. */
inf
};
/** \brief Return structure for `parse_digits`.
*
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) used.
*/
template<typename FloatType, typename CharType> struct digit_parse_result
{
/** \brief Pre-initializes members to sane values. */
digit_parse_result() : value(), K(0), str_end(nullptr), frac(0), special(), sign(true) { }
/** \brief Parsed mantissa value. */
typename diy_fp<FloatType>::mantissa_storage_type value;
/** \brief Decimal exponent, as calculated by exponent part and decimal
* point position.
*/
int K;
/** \brief Pointer to the memory after the parsed part of the buffer. */
const CharType* str_end;
/** \brief Binary numerator of fractional part, to help correct rounding. */
unsigned char frac;
/** \brief Flag of special value possibly occured. */
speciality special;
/** \brief Sign of the value. */
bool sign;
};
/** \brief Unified method to extract and parse digits in one pass.
*
* Goes through the string representation of the floating point value in
* the specified buffer, detects the meaning of each digit in its position
* and calculates main parts of floating point value mantissa, exponent,
* sign, fractional part.
*
* \tparam kappa maximum number of digits to expect.
* \tparam calc_frac if `true`, try to calculate fractional part, if any.
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) \p **str**
* consists of.
*
* \param str Character buffer with floating point value representation to
* parse.
*
* \return `digit_parse_result` with the parsing results.
*/
template<typename FloatType, typename CharType>
inline digit_parse_result<FloatType, CharType> parse_digits(const CharType* str) noexcept
{
digit_parse_result<FloatType, CharType> ret;
constexpr std::size_t kappa = decimal_q<FloatType>;
std::vector<unsigned char> parsed_digits;
parsed_digits.reserve(kappa);
bool dot_set(false);
bool sign_set(false);
bool frac_calculated(false);
std::size_t pow_gain(0);
std::size_t zero_substring_length(0), fraction_digits_count(0);
bool go_to_beach(false);
std::size_t pos(0);
while(!go_to_beach)
{
const auto c = str[pos];
switch (c)
{
case '0':
if (!parsed_digits.empty() || dot_set)
{
++zero_substring_length;
pow_gain += !dot_set;
}
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (zero_substring_length && parsed_digits.size() < kappa)
{
const std::size_t spare_digits { kappa - parsed_digits.size() };
auto zero_copy_count = zero_substring_length;
auto pow_gain_reduced = pow_gain;
if (!parsed_digits.empty())
{
zero_copy_count = std::min(zero_substring_length, spare_digits);
pow_gain_reduced = std::min(pow_gain, spare_digits);
parsed_digits.insert(parsed_digits.end(), zero_copy_count, 0);
}
fraction_digits_count += zero_copy_count - pow_gain_reduced;
zero_substring_length -= zero_copy_count;
pow_gain -= pow_gain_reduced;
}
if (parsed_digits.size() < kappa)
{
parsed_digits.push_back(c - '0');
fraction_digits_count += dot_set;
}
else
{
if (!frac_calculated)
{
const std::size_t frac_suffix_size = parsed_digits.size() + zero_substring_length - kappa;
auto tail = extract_fraction_digits<fraction_decimal_digits>(str + pos - frac_suffix_size);
ret.frac = convert_numerator<fraction_decimal_digits, fraction_binary_digits>(tail);
frac_calculated = true;
}
pow_gain += !dot_set;
}
break;
case '.':
go_to_beach = dot_set;
dot_set = true;
break;
case 'n':
case 'N':
if (pos == sign_set)
{
const std::size_t eaten = eat_nan(str + pos + 1);
pos += eaten + 1;
if (eaten)
ret.special = speciality::nan;
}
go_to_beach = true;
break;
case 'i':
case 'I':
if (pos == sign_set)
{
const std::size_t eaten = eat_inf(str + pos + 1);
pos += eaten + 1;
if (eaten)
ret.special = speciality::inf;
}
go_to_beach = true;
break;
case '-':
case '+':
if (pos == 0)
{
ret.sign = static_cast<bool>('-' - c); // '+' => true, '-' => false
sign_set = true;
break;
}
// fall through
default:
go_to_beach = true;
break;
}
go_to_beach |= pos > maximum_offset;
++pos;
}
std::size_t pow(0);
for (auto rit = parsed_digits.rbegin(); rit != parsed_digits.rend(); ++rit)
ret.value += (*rit) * seq_pow<typename diy_fp<FloatType>::mantissa_storage_type, 10, decimal_q<FloatType>>(pow++);
ret.str_end = str + (pos - 1);
ret.K = pow_gain - fraction_digits_count;
return ret;
}
/** \brief Return structure for `parse_mantissa`.
*
* \tparam FloatType destination value floating point type.
* \tparam CharType character type (typically `char` or `wchar_t`) used.
*/
template<typename FloatType, typename CharType> struct mantissa_parse_result
{
/** \brief Calculated mantissa value. */
diy_fp<FloatType> value;
/** \brief Corrected value of decimal exponent value. */
int K;
/** \brief Pointer to the memory after the parsed part of the buffer. */
const CharType* str_end;
/** \brief Flag of special value. */
speciality special;
/** \brief Sign of the value. */
bool sign;
};
/** \brief Tides up results of `parse_digits` for **Krosh** to use.
*
* Packs mantissa value into `diy_fp` structure and performs the necessary
* rounding up according to the fractional part value.
*
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) \p **str**
* consists of.
*
* \param str Character buffer with floating point value representation to
* parse.
*
* \return `mantissa_parse_result` structure with the results of parsing
* and corrections.
*/
template<typename FloatType, typename CharType> inline mantissa_parse_result<FloatType, CharType> parse_mantissa(const CharType* str)
{
mantissa_parse_result<FloatType, CharType> ret;
const auto& digits_parts(parse_digits<FloatType>(str));
ret.special = digits_parts.special;
ret.str_end = digits_parts.str_end;
ret.sign = digits_parts.sign;
if (digits_parts.special == speciality::no)
{
ret.value = diy_fp<FloatType>(digits_parts.value, 0);
ret.K = digits_parts.K;
if (digits_parts.value)
{
auto& w(ret.value);
w.normalize();
// extract additional binary digits and round up gently
if (digits_parts.frac)
{
assert(w.exponent() > (-1) * static_cast<int>(fraction_binary_digits));
const std::size_t lsb_pow(fraction_binary_digits + w.exponent());
typename diy_fp<FloatType>::mantissa_storage_type f(w.mantissa());
f |= digits_parts.frac >> lsb_pow;
w = diy_fp<FloatType>(f, w.exponent());
// round correctly avoiding integer overflow, undefined behaviour, pain and suffering
if (round_up(digits_parts.frac, lsb_pow).value)
{
++w;
}
}
}
}
return ret;
}
/** \brief Return structure for `parse_exponent`.
*
* \tparam CharType character type (typically `char` or `wchar_t`) used.
*/
template<typename CharType> struct exponent_parse_result
{
/** \brief Value of the exponent. */
int value;
/** \brief Pointer to the memory after the parsed part of the buffer. */
const CharType* str_end;
};
/** \brief Parses exponent part of the floating point string representation.
*
* \tparam CharType character type (typically `char` or `wchar_t`) of \p **str**.
*
* \param str Exponent part of character buffer with floating point value
* representation to parse.
*
* \return `exponent_parse_result` structure with parse results.
*/
template<typename CharType> inline exponent_parse_result<CharType> parse_exponent(const CharType* str)
{
exponent_parse_result<CharType> ret;
if (*str != 'e' && *str != 'E')
{
ret.value = 0;
ret.str_end = str;
}
else
{
++str;
const auto& digit_parts(parse_digits<float>(str));
ret.value = digit_parts.value * seq_pow<int, 10, exponent_decimal_digits>(digit_parts.K);
if (!digit_parts.sign)
ret.value = -ret.value;
ret.str_end = digit_parts.str_end;
}
return ret;
}
/** \brief Return structure, containing **Krosh** algorithm results.
*
* \tparam FloatType destination type of floating point value to store the
* results.
* \tparam CharType character type (typically `char` or `wchar_t`) used.
*/
template<typename FloatType, typename CharType> struct krosh_result
{
/** \brief The result floating point value, downsampled to the defined
* floating point type.
*/
FloatType value;
/** \brief Pointer to the memory after the parsed part of the buffer. */
const CharType* str_end;
/** \brief Status of the performed conversion. */
conversion_status status;
/** \brief Flag indicating if the result ensured to be rounded correctly. */
bool is_accurate;
};
/** \brief Implements **Krosh** algorithm.
*
* \tparam FloatType destination type of floating point value to store the
* results.
*
* \tparam CharType character type (typically `char` or `wchar_t`) \p **str**
* consists of.
*
* \param str Character buffer with floating point value
* representation to parse.
*
* \return `krosh_result` structure with all the results of **Krosh**
* algorithm.
*/
template<typename FloatType, typename CharType> krosh_result<FloatType, CharType> krosh(const CharType* str)
{
krosh_result<FloatType, CharType> ret;
static_assert(sizeof(FloatType) <= sizeof(typename diy_fp<FloatType>::mantissa_storage_type), "Only floating point types no longer, than 64 bits are supported.");
auto mp(parse_mantissa<FloatType>(str));
if (mp.special == speciality::no && mp.value.mantissa())
{
diy_fp<FloatType>& w(mp.value);
const auto& ep(parse_exponent(mp.str_end));
mp.K += ep.value;
if (mp.K)
{
const bool b1 = mp.K >= powers_ten<FloatType>::boundaries.first;
const bool b2 = mp.K <= powers_ten<FloatType>::boundaries.second;
if (b1 && b2)
{
w *= cached_power<FloatType>(mp.K);
}
else
{
if (!b1)
{
ret.value = FloatType(0);
ret.status = conversion_status::underflow;
}
else // !b2
{
ret.value = huge_value<FloatType>();
ret.status = conversion_status::overflow;
}
ret.str_end = ep.str_end;
ret.is_accurate = true;
return ret;
}
}
w.normalize();
const auto& v(w.downsample());
ret.value = v.value;
ret.str_end = ep.str_end;
ret.is_accurate = v.is_accurate;
ret.status = v.status;
}
else
{
switch (mp.special)
{
case speciality::nan:
ret.value = std::numeric_limits<FloatType>::quiet_NaN();
break;
case speciality::inf:
ret.value = std::numeric_limits<FloatType>::infinity();
break;
default:
ret.value = 0;
break;
}
ret.str_end = mp.str_end;
ret.is_accurate = true;
ret.status = conversion_status::success;
}
if (!mp.sign)
ret.value = -ret.value;
return ret;
}
}
#endif // FLOAXIE_CROSH_H

View File

@ -0,0 +1,61 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_MEMWRAP_H
#define FLOAXIE_MEMWRAP_H
#include <cstring>
#include <cwchar>
namespace floaxie
{
namespace wrap
{
/** \brief wrapper to template `std::(w)memset` by character type.
*
* \tparam CharType character type used.
*/
template<typename CharType> inline CharType* memset(CharType* dest, CharType ch, std::size_t count);
template<> inline char* memset(char* dest, char ch, std::size_t count)
{
return static_cast<char*>(std::memset(dest, ch, count));
}
template<> inline wchar_t* memset(wchar_t* dest, wchar_t ch, std::size_t count)
{
return std::wmemset(dest, ch, count);
}
/** \brief wrapper to template `std::(w)memmove` by character type.
*
* \tparam CharType character type used.
*/
template<typename CharType> inline CharType* memmove(CharType* dest, const CharType* src, std::size_t count);
template<> inline char* memmove(char* dest, const char* src, std::size_t count)
{
return static_cast<char*>(std::memmove(dest, src, count));
}
template<> inline wchar_t* memmove(wchar_t* dest, const wchar_t* src, std::size_t count)
{
return std::wmemmove(dest, src, count);
}
}
}
#endif // FLOAXIE_MEMWRAP_H

View File

@ -0,0 +1,47 @@
/*
* Copyright 2015-2017 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* cached power literals and cached_power() function use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_POWERS_TEN_H
#define FLOAXIE_POWERS_TEN_H
namespace floaxie
{
/** \brief Structure template to store compile-time values of powers of ten.
*
* The template represents a structure family, which stores pre-calculated
* values of significands and exponents of powers of ten, represented in
* integer form of the specified precision.
*
* The exact values are written in specializations of the template for
* single precision or double precision floating point type.
*
* The template specializations are used in **Grisu** and **Krosh** algorithms
* to get the pre-calculated cached power of ten.
*
* \tparam FloatType floating point type, for which precision the values are
* calculated.
*/
template<typename FloatType> struct powers_ten;
}
#endif // FLOAXIE_POWERS_TEN_H

View File

@ -0,0 +1,419 @@
/*
* Copyright 2015-2017 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* cached power literals and cached_power() function use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_POWERS_TEN_DOUBLE_H
#define FLOAXIE_POWERS_TEN_DOUBLE_H
#include <cstddef>
#include <array>
#include <external/floaxie/floaxie/powers_ten.h>
namespace floaxie
{
/** \brief Specialization of **powers_ten** for double precision
* floating point type (`double`).
*
* Significand (mantissa) type is `std::uint64_t`, exponent type is
* `int` (typically 32 bit, at least 16 bit).
*
* Values are calculated for powers of 10 in the range of [-343, 343]
* exponent.
*/
template<> struct powers_ten<double>
{
/** \brief Pre-calculated binary 64-bit representation of mantissa of
* powers of 10 in the range of [-343, 343] for single precision
* floating point values.
*/
static constexpr std::uint64_t f[] =
{
0xbf29dcaba82fdeae , 0xeef453d6923bd65a , 0x9558b4661b6565f8 , 0xbaaee17fa23ebf76 ,
0xe95a99df8ace6f54 , 0x91d8a02bb6c10594 , 0xb64ec836a47146fa , 0xe3e27a444d8d98b8 ,
0x8e6d8c6ab0787f73 , 0xb208ef855c969f50 , 0xde8b2b66b3bc4724 , 0x8b16fb203055ac76 ,
0xaddcb9e83c6b1794 , 0xd953e8624b85dd79 , 0x87d4713d6f33aa6c , 0xa9c98d8ccb009506 ,
0xd43bf0effdc0ba48 , 0x84a57695fe98746d , 0xa5ced43b7e3e9188 , 0xcf42894a5dce35ea ,
0x818995ce7aa0e1b2 , 0xa1ebfb4219491a1f , 0xca66fa129f9b60a7 , 0xfd00b897478238d1 ,
0x9e20735e8cb16382 , 0xc5a890362fddbc63 , 0xf712b443bbd52b7c , 0x9a6bb0aa55653b2d ,
0xc1069cd4eabe89f9 , 0xf148440a256e2c77 , 0x96cd2a865764dbca , 0xbc807527ed3e12bd ,
0xeba09271e88d976c , 0x93445b8731587ea3 , 0xb8157268fdae9e4c , 0xe61acf033d1a45df ,
0x8fd0c16206306bac , 0xb3c4f1ba87bc8697 , 0xe0b62e2929aba83c , 0x8c71dcd9ba0b4926 ,
0xaf8e5410288e1b6f , 0xdb71e91432b1a24b , 0x892731ac9faf056f , 0xab70fe17c79ac6ca ,
0xd64d3d9db981787d , 0x85f0468293f0eb4e , 0xa76c582338ed2622 , 0xd1476e2c07286faa ,
0x82cca4db847945ca , 0xa37fce126597973d , 0xcc5fc196fefd7d0c , 0xff77b1fcbebcdc4f ,
0x9faacf3df73609b1 , 0xc795830d75038c1e , 0xf97ae3d0d2446f25 , 0x9becce62836ac577 ,
0xc2e801fb244576d5 , 0xf3a20279ed56d48a , 0x9845418c345644d7 , 0xbe5691ef416bd60c ,
0xedec366b11c6cb8f , 0x94b3a202eb1c3f39 , 0xb9e08a83a5e34f08 , 0xe858ad248f5c22ca ,
0x91376c36d99995be , 0xb58547448ffffb2e , 0xe2e69915b3fff9f9 , 0x8dd01fad907ffc3c ,
0xb1442798f49ffb4b , 0xdd95317f31c7fa1d , 0x8a7d3eef7f1cfc52 , 0xad1c8eab5ee43b67 ,
0xd863b256369d4a41 , 0x873e4f75e2224e68 , 0xa90de3535aaae202 , 0xd3515c2831559a83 ,
0x8412d9991ed58092 , 0xa5178fff668ae0b6 , 0xce5d73ff402d98e4 , 0x80fa687f881c7f8e ,
0xa139029f6a239f72 , 0xc987434744ac874f , 0xfbe9141915d7a922 , 0x9d71ac8fada6c9b5 ,
0xc4ce17b399107c23 , 0xf6019da07f549b2b , 0x99c102844f94e0fb , 0xc0314325637a193a ,
0xf03d93eebc589f88 , 0x96267c7535b763b5 , 0xbbb01b9283253ca3 , 0xea9c227723ee8bcb ,
0x92a1958a7675175f , 0xb749faed14125d37 , 0xe51c79a85916f485 , 0x8f31cc0937ae58d3 ,
0xb2fe3f0b8599ef08 , 0xdfbdcece67006ac9 , 0x8bd6a141006042be , 0xaecc49914078536d ,
0xda7f5bf590966849 , 0x888f99797a5e012d , 0xaab37fd7d8f58179 , 0xd5605fcdcf32e1d7 ,
0x855c3be0a17fcd26 , 0xa6b34ad8c9dfc070 , 0xd0601d8efc57b08c , 0x823c12795db6ce57 ,
0xa2cb1717b52481ed , 0xcb7ddcdda26da269 , 0xfe5d54150b090b03 , 0x9efa548d26e5a6e2 ,
0xc6b8e9b0709f109a , 0xf867241c8cc6d4c1 , 0x9b407691d7fc44f8 , 0xc21094364dfb5637 ,
0xf294b943e17a2bc4 , 0x979cf3ca6cec5b5b , 0xbd8430bd08277231 , 0xece53cec4a314ebe ,
0x940f4613ae5ed137 , 0xb913179899f68584 , 0xe757dd7ec07426e5 , 0x9096ea6f3848984f ,
0xb4bca50b065abe63 , 0xe1ebce4dc7f16dfc , 0x8d3360f09cf6e4bd , 0xb080392cc4349ded ,
0xdca04777f541c568 , 0x89e42caaf9491b61 , 0xac5d37d5b79b6239 , 0xd77485cb25823ac7 ,
0x86a8d39ef77164bd , 0xa8530886b54dbdec , 0xd267caa862a12d67 , 0x8380dea93da4bc60 ,
0xa46116538d0deb78 , 0xcd795be870516656 , 0x806bd9714632dff6 , 0xa086cfcd97bf97f4 ,
0xc8a883c0fdaf7df0 , 0xfad2a4b13d1b5d6c , 0x9cc3a6eec6311a64 , 0xc3f490aa77bd60fd ,
0xf4f1b4d515acb93c , 0x991711052d8bf3c5 , 0xbf5cd54678eef0b7 , 0xef340a98172aace5 ,
0x9580869f0e7aac0f , 0xbae0a846d2195713 , 0xe998d258869facd7 , 0x91ff83775423cc06 ,
0xb67f6455292cbf08 , 0xe41f3d6a7377eeca , 0x8e938662882af53e , 0xb23867fb2a35b28e ,
0xdec681f9f4c31f31 , 0x8b3c113c38f9f37f , 0xae0b158b4738705f , 0xd98ddaee19068c76 ,
0x87f8a8d4cfa417ca , 0xa9f6d30a038d1dbc , 0xd47487cc8470652b , 0x84c8d4dfd2c63f3b ,
0xa5fb0a17c777cf0a , 0xcf79cc9db955c2cc , 0x81ac1fe293d599c0 , 0xa21727db38cb0030 ,
0xca9cf1d206fdc03c , 0xfd442e4688bd304b , 0x9e4a9cec15763e2f , 0xc5dd44271ad3cdba ,
0xf7549530e188c129 , 0x9a94dd3e8cf578ba , 0xc13a148e3032d6e8 , 0xf18899b1bc3f8ca2 ,
0x96f5600f15a7b7e5 , 0xbcb2b812db11a5de , 0xebdf661791d60f56 , 0x936b9fcebb25c996 ,
0xb84687c269ef3bfb , 0xe65829b3046b0afa , 0x8ff71a0fe2c2e6dc , 0xb3f4e093db73a093 ,
0xe0f218b8d25088b8 , 0x8c974f7383725573 , 0xafbd2350644eead0 , 0xdbac6c247d62a584 ,
0x894bc396ce5da772 , 0xab9eb47c81f5114f , 0xd686619ba27255a3 , 0x8613fd0145877586 ,
0xa798fc4196e952e7 , 0xd17f3b51fca3a7a1 , 0x82ef85133de648c5 , 0xa3ab66580d5fdaf6 ,
0xcc963fee10b7d1b3 , 0xffbbcfe994e5c620 , 0x9fd561f1fd0f9bd4 , 0xc7caba6e7c5382c9 ,
0xf9bd690a1b68637b , 0x9c1661a651213e2d , 0xc31bfa0fe5698db8 , 0xf3e2f893dec3f126 ,
0x986ddb5c6b3a76b8 , 0xbe89523386091466 , 0xee2ba6c0678b597f , 0x94db483840b717f0 ,
0xba121a4650e4ddec , 0xe896a0d7e51e1566 , 0x915e2486ef32cd60 , 0xb5b5ada8aaff80b8 ,
0xe3231912d5bf60e6 , 0x8df5efabc5979c90 , 0xb1736b96b6fd83b4 , 0xddd0467c64bce4a1 ,
0x8aa22c0dbef60ee4 , 0xad4ab7112eb3929e , 0xd89d64d57a607745 , 0x87625f056c7c4a8b ,
0xa93af6c6c79b5d2e , 0xd389b47879823479 , 0x843610cb4bf160cc , 0xa54394fe1eedb8ff ,
0xce947a3da6a9273e , 0x811ccc668829b887 , 0xa163ff802a3426a9 , 0xc9bcff6034c13053 ,
0xfc2c3f3841f17c68 , 0x9d9ba7832936edc1 , 0xc5029163f384a931 , 0xf64335bcf065d37d ,
0x99ea0196163fa42e , 0xc06481fb9bcf8d3a , 0xf07da27a82c37088 , 0x964e858c91ba2655 ,
0xbbe226efb628afeb , 0xeadab0aba3b2dbe5 , 0x92c8ae6b464fc96f , 0xb77ada0617e3bbcb ,
0xe55990879ddcaabe , 0x8f57fa54c2a9eab7 , 0xb32df8e9f3546564 , 0xdff9772470297ebd ,
0x8bfbea76c619ef36 , 0xaefae51477a06b04 , 0xdab99e59958885c5 , 0x88b402f7fd75539b ,
0xaae103b5fcd2a882 , 0xd59944a37c0752a2 , 0x857fcae62d8493a5 , 0xa6dfbd9fb8e5b88f ,
0xd097ad07a71f26b2 , 0x825ecc24c8737830 , 0xa2f67f2dfa90563b , 0xcbb41ef979346bca ,
0xfea126b7d78186bd , 0x9f24b832e6b0f436 , 0xc6ede63fa05d3144 , 0xf8a95fcf88747d94 ,
0x9b69dbe1b548ce7d , 0xc24452da229b021c , 0xf2d56790ab41c2a3 , 0x97c560ba6b0919a6 ,
0xbdb6b8e905cb600f , 0xed246723473e3813 , 0x9436c0760c86e30c , 0xb94470938fa89bcf ,
0xe7958cb87392c2c3 , 0x90bd77f3483bb9ba , 0xb4ecd5f01a4aa828 , 0xe2280b6c20dd5232 ,
0x8d590723948a535f , 0xb0af48ec79ace837 , 0xdcdb1b2798182245 , 0x8a08f0f8bf0f156b ,
0xac8b2d36eed2dac6 , 0xd7adf884aa879177 , 0x86ccbb52ea94baeb , 0xa87fea27a539e9a5 ,
0xd29fe4b18e88640f , 0x83a3eeeef9153e89 , 0xa48ceaaab75a8e2b , 0xcdb02555653131b6 ,
0x808e17555f3ebf12 , 0xa0b19d2ab70e6ed6 , 0xc8de047564d20a8c , 0xfb158592be068d2f ,
0x9ced737bb6c4183d , 0xc428d05aa4751e4d , 0xf53304714d9265e0 , 0x993fe2c6d07b7fac ,
0xbf8fdb78849a5f97 , 0xef73d256a5c0f77d , 0x95a8637627989aae , 0xbb127c53b17ec159 ,
0xe9d71b689dde71b0 , 0x9226712162ab070e , 0xb6b00d69bb55c8d1 , 0xe45c10c42a2b3b06 ,
0x8eb98a7a9a5b04e3 , 0xb267ed1940f1c61c , 0xdf01e85f912e37a3 , 0x8b61313bbabce2c6 ,
0xae397d8aa96c1b78 , 0xd9c7dced53c72256 , 0x881cea14545c7575 , 0xaa242499697392d3 ,
0xd4ad2dbfc3d07788 , 0x84ec3c97da624ab5 , 0xa6274bbdd0fadd62 , 0xcfb11ead453994ba ,
0x81ceb32c4b43fcf5 , 0xa2425ff75e14fc32 , 0xcad2f7f5359a3b3e , 0xfd87b5f28300ca0e ,
0x9e74d1b791e07e48 , 0xc612062576589ddb , 0xf79687aed3eec551 , 0x9abe14cd44753b53 ,
0xc16d9a0095928a27 , 0xf1c90080baf72cb1 , 0x971da05074da7bef , 0xbce5086492111aeb ,
0xec1e4a7db69561a5 , 0x9392ee8e921d5d07 , 0xb877aa3236a4b449 , 0xe69594bec44de15b ,
0x901d7cf73ab0acd9 , 0xb424dc35095cd80f , 0xe12e13424bb40e13 , 0x8cbccc096f5088cc ,
0xafebff0bcb24aaff , 0xdbe6fecebdedd5bf , 0x89705f4136b4a597 , 0xabcc77118461cefd ,
0xd6bf94d5e57a42bc , 0x8637bd05af6c69b6 , 0xa7c5ac471b478423 , 0xd1b71758e219652c ,
0x83126e978d4fdf3b , 0xa3d70a3d70a3d70a , 0xcccccccccccccccd , 0x8000000000000000 ,
0xa000000000000000 , 0xc800000000000000 , 0xfa00000000000000 , 0x9c40000000000000 ,
0xc350000000000000 , 0xf424000000000000 , 0x9896800000000000 , 0xbebc200000000000 ,
0xee6b280000000000 , 0x9502f90000000000 , 0xba43b74000000000 , 0xe8d4a51000000000 ,
0x9184e72a00000000 , 0xb5e620f480000000 , 0xe35fa931a0000000 , 0x8e1bc9bf04000000 ,
0xb1a2bc2ec5000000 , 0xde0b6b3a76400000 , 0x8ac7230489e80000 , 0xad78ebc5ac620000 ,
0xd8d726b7177a8000 , 0x878678326eac9000 , 0xa968163f0a57b400 , 0xd3c21bcecceda100 ,
0x84595161401484a0 , 0xa56fa5b99019a5c8 , 0xcecb8f27f4200f3a , 0x813f3978f8940984 ,
0xa18f07d736b90be5 , 0xc9f2c9cd04674edf , 0xfc6f7c4045812296 , 0x9dc5ada82b70b59e ,
0xc5371912364ce305 , 0xf684df56c3e01bc7 , 0x9a130b963a6c115c , 0xc097ce7bc90715b3 ,
0xf0bdc21abb48db20 , 0x96769950b50d88f4 , 0xbc143fa4e250eb31 , 0xeb194f8e1ae525fd ,
0x92efd1b8d0cf37be , 0xb7abc627050305ae , 0xe596b7b0c643c719 , 0x8f7e32ce7bea5c70 ,
0xb35dbf821ae4f38c , 0xe0352f62a19e306f , 0x8c213d9da502de45 , 0xaf298d050e4395d7 ,
0xdaf3f04651d47b4c , 0x88d8762bf324cd10 , 0xab0e93b6efee0054 , 0xd5d238a4abe98068 ,
0x85a36366eb71f041 , 0xa70c3c40a64e6c52 , 0xd0cf4b50cfe20766 , 0x82818f1281ed44a0 ,
0xa321f2d7226895c8 , 0xcbea6f8ceb02bb3a , 0xfee50b7025c36a08 , 0x9f4f2726179a2245 ,
0xc722f0ef9d80aad6 , 0xf8ebad2b84e0d58c , 0x9b934c3b330c8577 , 0xc2781f49ffcfa6d5 ,
0xf316271c7fc3908b , 0x97edd871cfda3a57 , 0xbde94e8e43d0c8ec , 0xed63a231d4c4fb27 ,
0x945e455f24fb1cf9 , 0xb975d6b6ee39e437 , 0xe7d34c64a9c85d44 , 0x90e40fbeea1d3a4b ,
0xb51d13aea4a488dd , 0xe264589a4dcdab15 , 0x8d7eb76070a08aed , 0xb0de65388cc8ada8 ,
0xdd15fe86affad912 , 0x8a2dbf142dfcc7ab , 0xacb92ed9397bf996 , 0xd7e77a8f87daf7fc ,
0x86f0ac99b4e8dafd , 0xa8acd7c0222311bd , 0xd2d80db02aabd62c , 0x83c7088e1aab65db ,
0xa4b8cab1a1563f52 , 0xcde6fd5e09abcf27 , 0x80b05e5ac60b6178 , 0xa0dc75f1778e39d6 ,
0xc913936dd571c84c , 0xfb5878494ace3a5f , 0x9d174b2dcec0e47b , 0xc45d1df942711d9a ,
0xf5746577930d6501 , 0x9968bf6abbe85f20 , 0xbfc2ef456ae276e9 , 0xefb3ab16c59b14a3 ,
0x95d04aee3b80ece6 , 0xbb445da9ca61281f , 0xea1575143cf97227 , 0x924d692ca61be758 ,
0xb6e0c377cfa2e12e , 0xe498f455c38b997a , 0x8edf98b59a373fec , 0xb2977ee300c50fe7 ,
0xdf3d5e9bc0f653e1 , 0x8b865b215899f46d , 0xae67f1e9aec07188 , 0xda01ee641a708dea ,
0x884134fe908658b2 , 0xaa51823e34a7eedf , 0xd4e5e2cdc1d1ea96 , 0x850fadc09923329e ,
0xa6539930bf6bff46 , 0xcfe87f7cef46ff17 , 0x81f14fae158c5f6e , 0xa26da3999aef774a ,
0xcb090c8001ab551c , 0xfdcb4fa002162a63 , 0x9e9f11c4014dda7e , 0xc646d63501a1511e ,
0xf7d88bc24209a565 , 0x9ae757596946075f , 0xc1a12d2fc3978937 , 0xf209787bb47d6b85 ,
0x9745eb4d50ce6333 , 0xbd176620a501fc00 , 0xec5d3fa8ce427b00 , 0x93ba47c980e98ce0 ,
0xb8a8d9bbe123f018 , 0xe6d3102ad96cec1e , 0x9043ea1ac7e41393 , 0xb454e4a179dd1877 ,
0xe16a1dc9d8545e95 , 0x8ce2529e2734bb1d , 0xb01ae745b101e9e4 , 0xdc21a1171d42645d ,
0x899504ae72497eba , 0xabfa45da0edbde69 , 0xd6f8d7509292d603 , 0x865b86925b9bc5c2 ,
0xa7f26836f282b733 , 0xd1ef0244af2364ff , 0x8335616aed761f1f , 0xa402b9c5a8d3a6e7 ,
0xcd036837130890a1 , 0x802221226be55a65 , 0xa02aa96b06deb0fe , 0xc83553c5c8965d3d ,
0xfa42a8b73abbf48d , 0x9c69a97284b578d8 , 0xc38413cf25e2d70e , 0xf46518c2ef5b8cd1 ,
0x98bf2f79d5993803 , 0xbeeefb584aff8604 , 0xeeaaba2e5dbf6785 , 0x952ab45cfa97a0b3 ,
0xba756174393d88e0 , 0xe912b9d1478ceb17 , 0x91abb422ccb812ef , 0xb616a12b7fe617aa ,
0xe39c49765fdf9d95 , 0x8e41ade9fbebc27d , 0xb1d219647ae6b31c , 0xde469fbd99a05fe3 ,
0x8aec23d680043bee , 0xada72ccc20054aea , 0xd910f7ff28069da4 , 0x87aa9aff79042287 ,
0xa99541bf57452b28 , 0xd3fa922f2d1675f2 , 0x847c9b5d7c2e09b7 , 0xa59bc234db398c25 ,
0xcf02b2c21207ef2f , 0x8161afb94b44f57d , 0xa1ba1ba79e1632dc , 0xca28a291859bbf93 ,
0xfcb2cb35e702af78 , 0x9defbf01b061adab , 0xc56baec21c7a1916 , 0xf6c69a72a3989f5c ,
0x9a3c2087a63f6399 , 0xc0cb28a98fcf3c80 , 0xf0fdf2d3f3c30b9f , 0x969eb7c47859e744 ,
0xbc4665b596706115 , 0xeb57ff22fc0c795a , 0x9316ff75dd87cbd8 , 0xb7dcbf5354e9bece ,
0xe5d3ef282a242e82 , 0x8fa475791a569d11 , 0xb38d92d760ec4455 , 0xe070f78d3927556b ,
0x8c469ab843b89563 , 0xaf58416654a6babb , 0xdb2e51bfe9d0696a , 0x88fcf317f22241e2 ,
0xab3c2fddeeaad25b , 0xd60b3bd56a5586f2 , 0x85c7056562757457 , 0xa738c6bebb12d16d ,
0xd106f86e69d785c8 , 0x82a45b450226b39d , 0xa34d721642b06084 , 0xcc20ce9bd35c78a5 ,
0xff290242c83396ce , 0x9f79a169bd203e41 , 0xc75809c42c684dd1 , 0xf92e0c3537826146 ,
0x9bbcc7a142b17ccc , 0xc2abf989935ddbfe , 0xf356f7ebf83552fe , 0x98165af37b2153df ,
0xbe1bf1b059e9a8d6 , 0xeda2ee1c7064130c , 0x9485d4d1c63e8be8 , 0xb9a74a0637ce2ee1 ,
0xe8111c87c5c1ba9a , 0x910ab1d4db9914a0 , 0xb54d5e4a127f59c8 , 0xe2a0b5dc971f303a ,
0x8da471a9de737e24 , 0xb10d8e1456105dad , 0xdd50f1996b947519 , 0x8a5296ffe33cc930 ,
0xace73cbfdc0bfb7b , 0xd8210befd30efa5a , 0x8714a775e3e95c78 , 0xa8d9d1535ce3b396 ,
0xd31045a8341ca07c , 0x83ea2b892091e44e , 0xa4e4b66b68b65d61 , 0xce1de40642e3f4b9 ,
0x80d2ae83e9ce78f4 , 0xa1075a24e4421731 , 0xc94930ae1d529cfd , 0xfb9b7cd9a4a7443c ,
0x9d412e0806e88aa6 , 0xc491798a08a2ad4f , 0xf5b5d7ec8acb58a3 , 0x9991a6f3d6bf1766 ,
0xbff610b0cc6edd3f , 0xeff394dcff8a948f , 0x95f83d0a1fb69cd9 , 0xbb764c4ca7a44410 ,
0xea53df5fd18d5514 , 0x92746b9be2f8552c , 0xb7118682dbb66a77 , 0xe4d5e82392a40515 ,
0x8f05b1163ba6832d , 0xb2c71d5bca9023f8 , 0xdf78e4b2bd342cf7 , 0x8bab8eefb6409c1a ,
0xae9672aba3d0c321 , 0xda3c0f568cc4f3e9 , 0x8865899617fb1871 , 0xaa7eebfb9df9de8e ,
0xd51ea6fa85785631 , 0x8533285c936b35df , 0xa67ff273b8460357 , 0xd01fef10a657842c ,
0x8213f56a67f6b29c , 0xa298f2c501f45f43 , 0xcb3f2f7642717713 , 0xfe0efb53d30dd4d8 ,
0x9ec95d1463e8a507 , 0xc67bb4597ce2ce49 , 0xf81aa16fdc1b81db , 0x9b10a4e5e9913129 ,
0xc1d4ce1f63f57d73 , 0xf24a01a73cf2dcd0 , 0x976e41088617ca02 , 0xbd49d14aa79dbc82 ,
0xec9c459d51852ba3 , 0x93e1ab8252f33b46 , 0xb8da1662e7b00a17 , 0xe7109bfba19c0c9d ,
0x906a617d450187e2 , 0xb484f9dc9641e9db , 0xe1a63853bbd26451 , 0x8d07e33455637eb3 ,
0xb049dc016abc5e60 , 0xdc5c5301c56b75f7 , 0x89b9b3e11b6329bb , 0xac2820d9623bf429 ,
0xd732290fbacaf134 , 0x867f59a9d4bed6c0 , 0xa81f301449ee8c70 , 0xd226fc195c6a2f8c ,
0x83585d8fd9c25db8 , 0xa42e74f3d032f526 , 0xcd3a1230c43fb26f , 0x80444b5e7aa7cf85 ,
0xa0555e361951c367 , 0xc86ab5c39fa63441 , 0xfa856334878fc151 , 0x9c935e00d4b9d8d2 ,
0xc3b8358109e84f07 , 0xf4a642e14c6262c9 , 0x98e7e9cccfbd7dbe , 0xbf21e44003acdd2d ,
0xeeea5d5004981478 , 0x95527a5202df0ccb , 0xbaa718e68396cffe , 0xe950df20247c83fd ,
0x91d28b7416cdd27e , 0xb6472e511c81471e , 0xe3d8f9e563a198e5 , 0x8e679c2f5e44ff8f ,
0xb201833b35d63f73 , 0xde81e40a034bcf50 , 0x8b112e86420f6192 , 0xadd57a27d29339f6 ,
0xd94ad8b1c7380874 , 0x87cec76f1c830549 , 0xa9c2794ae3a3c69b , 0xd433179d9c8cb841 ,
0x849feec281d7f329 , 0xa5c7ea73224deff3 , 0xcf39e50feae16bf0 , 0x81842f29f2cce376 ,
0xa1e53af46f801c53 , 0xca5e89b18b602368 , 0xfcf62c1dee382c42 , 0x9e19db92b4e31ba9 ,
0xc5a05277621be294 , 0xf70867153aa2db39 , 0x9a65406d44a5c903 , 0xc0fe908895cf3b44 ,
0xf13e34aabb430a15 , 0x96c6e0eab509e64d , 0xbc789925624c5fe1 , 0xeb96bf6ebadf77d9 ,
0x933e37a534cbaae8 , 0xb80dc58e81fe95a1 , 0xe61136f2227e3b0a , 0x8fcac257558ee4e6 ,
0xb3bd72ed2af29e20 , 0xe0accfa875af45a8 , 0x8c6c01c9498d8b89 , 0xaf87023b9bf0ee6b ,
0xdb68c2ca82ed2a06 , 0x892179be91d43a44 , 0xab69d82e364948d4
};
/** \brief Pre-calculated values of binary exponent of powers of 10 in the
* range of [-343, 343].
*/
static constexpr int e[] =
{
-1203 , -1200 , -1196 , -1193 ,
-1190 , -1186 , -1183 , -1180 ,
-1176 , -1173 , -1170 , -1166 ,
-1163 , -1160 , -1156 , -1153 ,
-1150 , -1146 , -1143 , -1140 ,
-1136 , -1133 , -1130 , -1127 ,
-1123 , -1120 , -1117 , -1113 ,
-1110 , -1107 , -1103 , -1100 ,
-1097 , -1093 , -1090 , -1087 ,
-1083 , -1080 , -1077 , -1073 ,
-1070 , -1067 , -1063 , -1060 ,
-1057 , -1053 , -1050 , -1047 ,
-1043 , -1040 , -1037 , -1034 ,
-1030 , -1027 , -1024 , -1020 ,
-1017 , -1014 , -1010 , -1007 ,
-1004 , -1000 , -997 , -994 ,
-990 , -987 , -984 , -980 ,
-977 , -974 , -970 , -967 ,
-964 , -960 , -957 , -954 ,
-950 , -947 , -944 , -940 ,
-937 , -934 , -931 , -927 ,
-924 , -921 , -917 , -914 ,
-911 , -907 , -904 , -901 ,
-897 , -894 , -891 , -887 ,
-884 , -881 , -877 , -874 ,
-871 , -867 , -864 , -861 ,
-857 , -854 , -851 , -847 ,
-844 , -841 , -838 , -834 ,
-831 , -828 , -824 , -821 ,
-818 , -814 , -811 , -808 ,
-804 , -801 , -798 , -794 ,
-791 , -788 , -784 , -781 ,
-778 , -774 , -771 , -768 ,
-764 , -761 , -758 , -754 ,
-751 , -748 , -744 , -741 ,
-738 , -735 , -731 , -728 ,
-725 , -721 , -718 , -715 ,
-711 , -708 , -705 , -701 ,
-698 , -695 , -691 , -688 ,
-685 , -681 , -678 , -675 ,
-671 , -668 , -665 , -661 ,
-658 , -655 , -651 , -648 ,
-645 , -642 , -638 , -635 ,
-632 , -628 , -625 , -622 ,
-618 , -615 , -612 , -608 ,
-605 , -602 , -598 , -595 ,
-592 , -588 , -585 , -582 ,
-578 , -575 , -572 , -568 ,
-565 , -562 , -558 , -555 ,
-552 , -549 , -545 , -542 ,
-539 , -535 , -532 , -529 ,
-525 , -522 , -519 , -515 ,
-512 , -509 , -505 , -502 ,
-499 , -495 , -492 , -489 ,
-485 , -482 , -479 , -475 ,
-472 , -469 , -465 , -462 ,
-459 , -455 , -452 , -449 ,
-446 , -442 , -439 , -436 ,
-432 , -429 , -426 , -422 ,
-419 , -416 , -412 , -409 ,
-406 , -402 , -399 , -396 ,
-392 , -389 , -386 , -382 ,
-379 , -376 , -372 , -369 ,
-366 , -362 , -359 , -356 ,
-353 , -349 , -346 , -343 ,
-339 , -336 , -333 , -329 ,
-326 , -323 , -319 , -316 ,
-313 , -309 , -306 , -303 ,
-299 , -296 , -293 , -289 ,
-286 , -283 , -279 , -276 ,
-273 , -269 , -266 , -263 ,
-259 , -256 , -253 , -250 ,
-246 , -243 , -240 , -236 ,
-233 , -230 , -226 , -223 ,
-220 , -216 , -213 , -210 ,
-206 , -203 , -200 , -196 ,
-193 , -190 , -186 , -183 ,
-180 , -176 , -173 , -170 ,
-166 , -163 , -160 , -157 ,
-153 , -150 , -147 , -143 ,
-140 , -137 , -133 , -130 ,
-127 , -123 , -120 , -117 ,
-113 , -110 , -107 , -103 ,
-100 , -97 , -93 , -90 ,
-87 , -83 , -80 , -77 ,
-73 , -70 , -67 , -63 ,
-60 , -57 , -54 , -50 ,
-47 , -44 , -40 , -37 ,
-34 , -30 , -27 , -24 ,
-20 , -17 , -14 , -10 ,
-7 , -4 , 0 , 3 ,
6 , 10 , 13 , 16 ,
20 , 23 , 26 , 30 ,
33 , 36 , 39 , 43 ,
46 , 49 , 53 , 56 ,
59 , 63 , 66 , 69 ,
73 , 76 , 79 , 83 ,
86 , 89 , 93 , 96 ,
99 , 103 , 106 , 109 ,
113 , 116 , 119 , 123 ,
126 , 129 , 132 , 136 ,
139 , 142 , 146 , 149 ,
152 , 156 , 159 , 162 ,
166 , 169 , 172 , 176 ,
179 , 182 , 186 , 189 ,
192 , 196 , 199 , 202 ,
206 , 209 , 212 , 216 ,
219 , 222 , 226 , 229 ,
232 , 235 , 239 , 242 ,
245 , 249 , 252 , 255 ,
259 , 262 , 265 , 269 ,
272 , 275 , 279 , 282 ,
285 , 289 , 292 , 295 ,
299 , 302 , 305 , 309 ,
312 , 315 , 319 , 322 ,
325 , 328 , 332 , 335 ,
338 , 342 , 345 , 348 ,
352 , 355 , 358 , 362 ,
365 , 368 , 372 , 375 ,
378 , 382 , 385 , 388 ,
392 , 395 , 398 , 402 ,
405 , 408 , 412 , 415 ,
418 , 422 , 425 , 428 ,
431 , 435 , 438 , 441 ,
445 , 448 , 451 , 455 ,
458 , 461 , 465 , 468 ,
471 , 475 , 478 , 481 ,
485 , 488 , 491 , 495 ,
498 , 501 , 505 , 508 ,
511 , 515 , 518 , 521 ,
524 , 528 , 531 , 534 ,
538 , 541 , 544 , 548 ,
551 , 554 , 558 , 561 ,
564 , 568 , 571 , 574 ,
578 , 581 , 584 , 588 ,
591 , 594 , 598 , 601 ,
604 , 608 , 611 , 614 ,
617 , 621 , 624 , 627 ,
631 , 634 , 637 , 641 ,
644 , 647 , 651 , 654 ,
657 , 661 , 664 , 667 ,
671 , 674 , 677 , 681 ,
684 , 687 , 691 , 694 ,
697 , 701 , 704 , 707 ,
711 , 714 , 717 , 720 ,
724 , 727 , 730 , 734 ,
737 , 740 , 744 , 747 ,
750 , 754 , 757 , 760 ,
764 , 767 , 770 , 774 ,
777 , 780 , 784 , 787 ,
790 , 794 , 797 , 800 ,
804 , 807 , 810 , 813 ,
817 , 820 , 823 , 827 ,
830 , 833 , 837 , 840 ,
843 , 847 , 850 , 853 ,
857 , 860 , 863 , 867 ,
870 , 873 , 877 , 880 ,
883 , 887 , 890 , 893 ,
897 , 900 , 903 , 907 ,
910 , 913 , 916 , 920 ,
923 , 926 , 930 , 933 ,
936 , 940 , 943 , 946 ,
950 , 953 , 956 , 960 ,
963 , 966 , 970 , 973 ,
976 , 980 , 983 , 986 ,
990 , 993 , 996 , 1000 ,
1003 , 1006 , 1009 , 1013 ,
1016 , 1019 , 1023 , 1026 ,
1029 , 1033 , 1036 , 1039 ,
1043 , 1046 , 1049 , 1053 ,
1056 , 1059 , 1063 , 1066 ,
1069 , 1073 , 1076
};
/** \brief Offsef of the values for zero power in the arrays.
*/
static constexpr std::size_t pow_0_offset = 343;
/** \brief Boundaries of possible powers of ten for the type.
*/
static constexpr std::pair<int, int> boundaries = { -343, 343 };
};
constexpr decltype(powers_ten<double>::f) powers_ten<double>::f;
constexpr decltype(powers_ten<double>::e) powers_ten<double>::e;
constexpr std::size_t powers_ten<double>::pow_0_offset;
constexpr std::pair<int, int> powers_ten<double>::boundaries;
}
#endif // FLOAXIE_POWERS_TEN_DOUBLE_H

View File

@ -0,0 +1,276 @@
/*
* Copyright 2015-2017 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* cached power literals and cached_power() function use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_POWERS_TEN_SINGLE_H
#define FLOAXIE_POWERS_TEN_SINGLE_H
#include <cstddef>
#include <external/floaxie/floaxie/powers_ten.h>
namespace floaxie
{
/** \brief Specialization of **powers_ten** for single precision
* floating point type (`float`).
*
* Significand (mantissa) type is `std::uint32_t`, exponent type is
* `int` (typically 32 bit, at least 16 bit).
*
* Values are calculated for powers of 10 in the range of [-50, 50]
* exponent.
*/
template<> struct powers_ten<float>
{
/** \brief Pre-calculated binary 32-bit representation of mantissa of
* powers of 10 in the range of [-50, 50] for single precision
* floating point values.
*/
static constexpr std::uint32_t f[] =
{
0xef73d257,
0x95a86376,
0xbb127c54,
0xe9d71b69,
0x92267121,
0xb6b00d6a,
0xe45c10c4,
0x8eb98a7b,
0xb267ed19,
0xdf01e860,
0x8b61313c,
0xae397d8b,
0xd9c7dced,
0x881cea14,
0xaa242499,
0xd4ad2dc0,
0x84ec3c98,
0xa6274bbe,
0xcfb11ead,
0x81ceb32c,
0xa2425ff7,
0xcad2f7f5,
0xfd87b5f3,
0x9e74d1b8,
0xc6120625,
0xf79687af,
0x9abe14cd,
0xc16d9a01,
0xf1c90081,
0x971da050,
0xbce50865,
0xec1e4a7e,
0x9392ee8f,
0xb877aa32,
0xe69594bf,
0x901d7cf7,
0xb424dc35,
0xe12e1342,
0x8cbccc09,
0xafebff0c,
0xdbe6fecf,
0x89705f41,
0xabcc7712,
0xd6bf94d6,
0x8637bd06,
0xa7c5ac47,
0xd1b71759,
0x83126e98,
0xa3d70a3d,
0xcccccccd,
0x80000000,
0xa0000000,
0xc8000000,
0xfa000000,
0x9c400000,
0xc3500000,
0xf4240000,
0x98968000,
0xbebc2000,
0xee6b2800,
0x9502f900,
0xba43b740,
0xe8d4a510,
0x9184e72a,
0xb5e620f4,
0xe35fa932,
0x8e1bc9bf,
0xb1a2bc2f,
0xde0b6b3a,
0x8ac72305,
0xad78ebc6,
0xd8d726b7,
0x87867832,
0xa968163f,
0xd3c21bcf,
0x84595161,
0xa56fa5ba,
0xcecb8f28,
0x813f3979,
0xa18f07d7,
0xc9f2c9cd,
0xfc6f7c40,
0x9dc5ada8,
0xc5371912,
0xf684df57,
0x9a130b96,
0xc097ce7c,
0xf0bdc21b,
0x96769951,
0xbc143fa5,
0xeb194f8e,
0x92efd1b9,
0xb7abc627,
0xe596b7b1,
0x8f7e32ce,
0xb35dbf82,
0xe0352f63,
0x8c213d9e,
0xaf298d05,
0xdaf3f046,
0x88d8762c
};
/** \brief Pre-calculated values of binary exponent of powers of 10 in the
* range of [-50, 50].
*/
static constexpr int e[] =
{
-198,
-194,
-191,
-188,
-184,
-181,
-178,
-174,
-171,
-168,
-164,
-161,
-158,
-154,
-151,
-148,
-144,
-141,
-138,
-134,
-131,
-128,
-125,
-121,
-118,
-115,
-111,
-108,
-105,
-101,
-98,
-95,
-91,
-88,
-85,
-81,
-78,
-75,
-71,
-68,
-65,
-61,
-58,
-55,
-51,
-48,
-45,
-41,
-38,
-35,
-31,
-28,
-25,
-22,
-18,
-15,
-12,
-8,
-5,
-2,
2,
5,
8,
12,
15,
18,
22,
25,
28,
32,
35,
38,
42,
45,
48,
52,
55,
58,
62,
65,
68,
71,
75,
78,
81,
85,
88,
91,
95,
98,
101,
105,
108,
111,
115,
118,
121,
125,
128,
131,
135
};
/** \brief Offsef of the values for zero power in the arrays.
*/
static constexpr std::size_t pow_0_offset = 50;
/** \brief Boundaries of possible powers of ten for the type.
*/
static constexpr std::pair<int, int> boundaries = { -50, 50 };
};
constexpr decltype(powers_ten<float>::f) powers_ten<float>::f;
constexpr decltype(powers_ten<float>::e) powers_ten<float>::e;
constexpr std::size_t powers_ten<float>::pow_0_offset;
constexpr std::pair<int, int> powers_ten<float>::boundaries;
}
#endif // FLOAXIE_POWERS_TEN_SINGLE_H

View File

@ -0,0 +1,221 @@
/*
* Copyright 2015-2022 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* prettify_string() and fill_exponent() functions use code taken from
* Florian Loitsch's original Grisu algorithms implementation
* (http://florian.loitsch.com/publications/bench.tar.gz)
* and "Printing Floating-Point Numbers Quickly and Accurately with
* Integers" paper
* (http://florian.loitsch.com/publications/dtoa-pldi2010.pdf)
*/
#ifndef FLOAXIE_PRETTIFY_H
#define FLOAXIE_PRETTIFY_H
#include <cstring>
#include <cassert>
#include <cstdlib>
#include <algorithm>
#include <external/floaxie/floaxie/static_pow.h>
#include <external/floaxie/floaxie/print.h>
#include <external/floaxie/floaxie/memwrap.h>
namespace floaxie
{
/** \brief Format enumeration.
*
*/
enum format {
/** \brief Decimal format. */
decimal,
/** \brief Decimal exponent (a.k.a. *scientific*) format. */
scientific
};
/** \brief LUT of two-digit decimal values to speed up their printing. */
constexpr const char digits_lut[200] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'
};
/** \brief Detects the more appropriate format to print based on
* \p **threshold** value.
* \tparam threshold the maximum number of digits in the string
* representation, when decimal format can be used (otherwise decimal
* exponent or *scientific* format is used).
*
* \return value of the chosen format.
* \see `format`
*/
template<std::size_t threshold> inline format choose_format(const std::size_t field_width) noexcept
{
static_assert(threshold > static_pow<10, 1>(), "Only 10 ⩽ |threshold| ⩽ 100 is supported");
return field_width > threshold ? format::scientific : format::decimal;
}
/** \brief Prints decimal exponent value.
*
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param K decimal exponent value.
* \param buffer character buffer to print to.
*
* \return number of characters written to the buffer.
*/
template<typename CharType> inline std::size_t fill_exponent(unsigned int K, CharType* buffer) noexcept
{
const unsigned char hundreds = static_cast<unsigned char>(K / 100);
K %= 100;
buffer[0] = '0' + hundreds;
buffer += (hundreds > 0);
const char* d = digits_lut + K * 2;
buffer[0] = d[0];
buffer[1] = d[1];
buffer[2] = '\0';
return 2 + (hundreds > 0);
}
/** \brief Prints exponent (*scientific*) part of value representation in
* decimal exponent format.
*
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param buffer character buffer with properly printed mantissa.
* \param len output parameter to return the length of printed
* representation.
* \param dot_pos number of character, where dot position should be placed.
*
* \return number of characters written to the buffer.
*
* \see `print_decimal()`
*/
template<typename CharType> inline std::size_t print_scientific(CharType* buffer, const unsigned int len, const int dot_pos) noexcept
{
const int K = dot_pos - 1;
if (len > 1)
{
/* leave the first digit. then add a '.' and at the end 'e...' */
wrap::memmove(buffer + 2, buffer + 1, len - 1);
buffer[1] = '.';
buffer += len;
}
/* add 'e...' */
buffer[1] = 'e';
buffer[2] = '-';
buffer += K < 0;
return len + /*dot*/(len > 1) + /*'e'*/1 + /*exp sign*/(K < 0) + fill_exponent(std::abs(K), buffer + 2);
}
/** \brief Formats decimal mantissa part of value representation.
*
* Tides up the printed digits in \p **buffer**, adding leading zeros,
* placing the decimal point into the proper place etc.
*
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param buffer character buffer with printed digits.
* \param len length of current representation in \p **buffer**.
* \param k decimal exponent of the value.
*
* \return number of characters written to the buffer.
*/
template<typename CharType> inline std::size_t print_decimal(CharType* buffer, const unsigned int len, const int k) noexcept
{
const int dot_pos = static_cast<int>(len) + k;
const unsigned int actual_dot_pos = dot_pos > 0 ? uint32_t(dot_pos) : 1;
const unsigned int left_offset = dot_pos > 0 ? 0 : 2 - uint32_t(dot_pos);
const unsigned int right_offset = positive_part(k);
const unsigned int left_shift_src = positive_part(dot_pos);
const unsigned int left_shift_dest = dot_pos > 0 ? left_shift_src + (k < 0) : left_offset;
const unsigned int left_shift_len = positive_part(static_cast<int>(len) - static_cast<int>(left_shift_src));
const unsigned int term_pos = len + right_offset + (left_shift_dest - left_shift_src);
wrap::memmove(buffer + left_shift_dest, buffer + left_shift_src, left_shift_len);
wrap::memset(buffer, CharType('0'), left_offset);
wrap::memset(buffer + len, CharType('0'), right_offset);
buffer[actual_dot_pos] = '.';
buffer[term_pos] = '\0';
return term_pos;
}
/** \brief Makes final format corrections to have the string representation
* be properly and pretty printed (with decimal point in place, exponent
* part, where appropriate etc.).
*
* \tparam decimal_scientific_threshold the maximum number of digits in the
* string representation, when decimal format can be used (otherwise
* decimal exponent or *scientific* format is used).
* \tparam CharType character type (typically `char` or `wchar_t`) of the
* output buffer \p **buffer**.
*
* \param buffer character buffer with printed digits.
* \param len length of current representation in \p **buffer**.
* \param k decimal exponent of the value.
*
* \return number of characters written to the buffer.
*
* \see `print_decimal()`
* \see `print_scientific()`
*/
template<std::size_t decimal_scientific_threshold, typename CharType>
inline std::size_t prettify(CharType* buffer, const unsigned int len, const int k) noexcept
{
/* v = buffer * 10 ^ k
dot_pos is such that 10 ^ (dot_pos - 1) <= v < 10 ^ dot_pos
this way dot_pos gives the position of the comma.
*/
const int dot_pos = static_cast<int>(len) + k;
// is always positive, since dot_pos is negative only when k is negative
const std::size_t field_width = size_t((std::max)(dot_pos, -k));
switch (choose_format<decimal_scientific_threshold>(field_width))
{
case format::decimal:
return print_decimal(buffer, len, k);
case format::scientific:
return print_scientific(buffer, len, dot_pos);
}
// never reach here
return 0;
}
}
#endif // FLOAXIE_PRETTIFY_H

View File

@ -0,0 +1,63 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* Several utility functions as printing binary representation of floating
* point values and other stuff.
*
*/
#ifndef FLOAXIE_PRINT_H
#define FLOAXIE_PRINT_H
#include <bitset>
#include <string>
#include <cstdint>
#include <external/floaxie/floaxie/type_punning_cast.h>
#include <external/floaxie/floaxie/bit_ops.h>
namespace floaxie
{
/** \brief Prints `double` value in binary format, splitting sign, exponent
* and mantissa parts with spaces.
*
* Useful for debugging purposes.
*/
std::string inline print_binary(double f)
{
auto s(std::bitset<64>(type_punning_cast<std::uint64_t>(f)).to_string());
s.insert(1, 1, ' ');
s.insert(13, 1, ' ');
return s;
}
/** \brief Print arbitrary numeric value in binary format. */
template<typename NumericType> inline std::string print_binary(NumericType v)
{
return std::bitset<bit_size<NumericType>()>(v).to_string();
}
/** \brief Print arbitrary numeric value as if it were `double`. */
template<typename NumericType> inline std::string print_double_presentation(NumericType v)
{
auto s(std::bitset<64>(v).to_string());
s.insert(1, 1, ' ');
s.insert(54, 1, ' ');
s.insert(56, 1, ' ');
return s;
}
}
#endif // FLOAXIE_PRINT_H

View File

@ -0,0 +1,164 @@
/*
* Copyright 2015-2019 Alexey Chernov <4ernov@gmail.com>
*
* 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 FLOAXIE_STATIC_POW_H
#define FLOAXIE_STATIC_POW_H
#include <array>
#include <utility>
namespace floaxie
{
/** \brief Helper class template to calculate integer positive power in
* compile time.
*
* Unspecialized template contains intermediate steps implementation.
*
* \tparam base base of the power.
* \tparam pow exponent to power base.
*/
template<unsigned int base, unsigned int pow> struct static_pow_helper
{
/** \brief Result of power calculation (in compile time).
*
* Is calculated recursively in intermediate steps.
*/
static const unsigned int value = base * static_pow_helper<base, pow - 1>::value;
};
/** \brief Terminal specialization of `static_pow_helper` template. */
template<unsigned int base> struct static_pow_helper<base, 0>
{
/** \brief Any value in zero power is `1`. */
static const unsigned int value = 1;
};
/** \brief Handy wrapper function to calculate positive power in compile
* time.
*
* \tparam base base of the power.
* \tparam pow exponent to power base.
*
* \return Result of power calculation (in compile time).
*/
template<unsigned int base, unsigned int pow> constexpr unsigned long static_pow()
{
static_assert(base > 0, "Base should be positive");
return static_pow_helper<base, pow>::value;
}
/** \brief Helper structure template to append value to
* `std::integer_sequence`.
*
* \tparam T type of elements of `std::integer_sequence`.
* \tparam Add element to add to the specified sequence.
* \tparam Seq original `std::integer_sequence` expressed by parameter pack
* of its elements in template parameters.
*/
template<typename T, T Add, typename Seq> struct concat_sequence;
/** \brief Implements the concatenation itself.
*
* Steals parameters (read: contents) of passed `std::integer_sequence`-like
* type and creates another `std::integer_sequence`-like type with the
* specified value appended at the end.
*/
template<typename T, T Add, T... Seq> struct concat_sequence<T, Add, std::integer_sequence<T, Seq...>>
{
/** \brief New `std::integer_sequence`-like type with the specified
* element added to the end.
*/
using type = std::integer_sequence<T, Seq..., Add>;
};
/** \brief Helper structure template to convert `std::integer_sequence`
* to `std::array`.
*
* \tparam Seq sequence to convert.
*/
template<typename Seq> struct make_integer_array;
/** \brief Main specialization of `make_integer_array` with the specified
* input `std::integer_sequence`.
*
* \tparam T type of elements of `std::integer_sequence`.
* \tparam Ints elements of the `std::integer_sequence` to convert.
*/
template<typename T, T... Ints> struct make_integer_array<std::integer_sequence<T, Ints...>>
{
/** \brief Type of the resulting `std::array` (specialized with
* element type and length). */
using type = std::array<T, sizeof...(Ints)>;
/** \brief Instance of the resulting `std::array` filled in with
* elements from the specified `std::integer_sequence` in compile time.
*/
static constexpr type value = type{{Ints...}};
};
/** \brief Creates `std::integer_sequence` with sequence of powers of
* \p **base** up to the exponent value, defined by \p **current_pow**.
*
* For example, if \p base is `10` and \p current_pow is `3`, the result
* will contain values `1000`, `100`, `10`, `1`.
*
* \tparam T type of elements.
* \tparam base base of powers to calculate.
* \tparam current_pow the maximum exponent value to calculate power for.
*/
template<typename T, T base, std::size_t current_pow> struct pow_sequencer
{
/** \brief Value of power on the current step (in recursion). */
static const T value = pow_sequencer<T, base, current_pow - 1>::value * base;
/** \brief `std::integer_sequence`-like type containing all the
* calculated powers at the moment.
*/
typedef typename concat_sequence<T, value, typename pow_sequencer<T, base, current_pow - 1>::sequence_type>::type sequence_type;
};
/** \brief Terminal step specialization for `pow_sequencer` template. */
template<typename T, T base> struct pow_sequencer<T, base, 0>
{
/** \brief Zero power of base. */
static constexpr T value = 1;
/** \brief `std::integer_sequence` with zero power only yet. */
typedef std::integer_sequence<T, 1> sequence_type;
};
/** \brief Handy wrapper function to calculate a sequence of powers.
*
* \tparam T type of elements.
* \tparam base base for powers to calculate.
* \tparam max_pow maximum exponent, up to which the calculation will be
* performed.
*
* \return `std::array` filled with powers of the specified arguments in
* reverse order. I.e. for \p **T** of `unsigned int`, \p **base** of 10
* and \p **max_pow** 3 this would be:
* `std::array<unsigned int>({{1000, 100, 10, 1}})`.
*/
template<typename T, T base, std::size_t max_pow> constexpr T seq_pow(std::size_t pow)
{
typedef make_integer_array<typename pow_sequencer<T, base, max_pow>::sequence_type> maker;
constexpr typename maker::type arr(maker::value);
return arr[pow];
}
}
#endif // FLOAXIE_STATIC_POW_H

View File

@ -0,0 +1,45 @@
/*
* Copyright 2015, 2016 Alexey Chernov <4ernov@gmail.com>
*
* 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.
*
* type_punning_cast is copy-paste of pseudo_cast as posted by plasmacel
* at StackOverflow
* (http://stackoverflow.com/questions/17789928/whats-a-proper-way-of-type-punning-a-float-to-an-int-and-vice-versa)
*
*/
#ifndef FLOAXIE_TYPE_PUNNING_CAST
#define FLOAXIE_TYPE_PUNNING_CAST
#include <cstring>
namespace floaxie
{
/** \brief Correct type-punning cast implementation to avoid any possible
* undefined behaviour.
*
* \see https://en.wikipedia.org/wiki/Type_punning
*/
template<typename T, typename U> inline T type_punning_cast(const U& x)
{
static_assert(sizeof(T) == sizeof(U),
"type_punning_cast can't handle types with different size");
T to;
std::memcpy(&to, &x, sizeof(T));
return to;
}
}
#endif // FLOAXIE_TYPE_PUNNING_CAST

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
// fpng.h - unlicense (see end of fpng.cpp)
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <vector>
#ifndef FPNG_TRAIN_HUFFMAN_TABLES
// Set to 1 when using the -t (training) option in fpng_test to generate new opaque/alpha Huffman tables for the single pass encoder.
#define FPNG_TRAIN_HUFFMAN_TABLES (0)
#endif
namespace fpng
{
// ---- Library initialization - call once to identify if the processor supports SSE.
// Otherwise you'll only get scalar fallbacks.
void fpng_init();
// ---- Useful Utilities
// Returns true if the CPU supports SSE 4.1, and SSE support wasn't disabled by setting FPNG_NO_SSE=1.
// fpng_init() must have been called first, or it'll assert and return false.
bool fpng_cpu_supports_sse41();
// Fast CRC-32 SSE4.1+pclmul or a scalar fallback (slice by 4)
const uint32_t FPNG_CRC32_INIT = 0;
uint32_t fpng_crc32(const void* pData, size_t size, uint32_t prev_crc32 = FPNG_CRC32_INIT);
// Fast Adler32 SSE4.1 Adler-32 with a scalar fallback.
const uint32_t FPNG_ADLER32_INIT = 1;
uint32_t fpng_adler32(const void* pData, size_t size, uint32_t adler = FPNG_ADLER32_INIT);
// ---- Compression
enum
{
// Enables computing custom Huffman tables for each file, instead of using the custom global tables.
// Results in roughly 6% smaller files on average, but compression is around 40% slower.
FPNG_ENCODE_SLOWER = 1,
// Only use raw Deflate blocks (no compression at all). Intended for testing.
FPNG_FORCE_UNCOMPRESSED = 2,
};
// Fast PNG encoding. The resulting file can be decoded either using a standard PNG decoder or by the fpng_decode_memory() function below.
// pImage: pointer to RGB or RGBA image pixels, R first in memory, B/A last.
// w/h - image dimensions. Image's row pitch in bytes must is w*num_chans.
// num_chans must be 3 or 4.
bool fpng_encode_image_to_memory(const void* pImage, uint32_t w, uint32_t h, uint32_t num_chans, std::vector<uint8_t>& out_buf, uint32_t flags = 0);
#ifndef FPNG_NO_STDIO
// Fast PNG encoding to the specified file.
bool fpng_encode_image_to_file(const char* pFilename, const void* pImage, uint32_t w, uint32_t h, uint32_t num_chans, uint32_t flags = 0);
#endif
// ---- Decompression
enum
{
FPNG_DECODE_SUCCESS = 0, // file is a valid PNG file and written by FPNG and the decode succeeded
FPNG_DECODE_NOT_FPNG, // file is a valid PNG file, but it wasn't written by FPNG so you should try decoding it with a general purpose PNG decoder
FPNG_DECODE_INVALID_ARG, // invalid function parameter
FPNG_DECODE_FAILED_NOT_PNG, // file cannot be a PNG file
FPNG_DECODE_FAILED_HEADER_CRC32, // a chunk CRC32 check failed, file is likely corrupted or not PNG
FPNG_DECODE_FAILED_INVALID_DIMENSIONS, // invalid image dimensions in IHDR chunk (0 or too large)
FPNG_DECODE_FAILED_DIMENSIONS_TOO_LARGE, // decoding the file fully into memory would likely require too much memory (only on 32bpp builds)
FPNG_DECODE_FAILED_CHUNK_PARSING, // failed while parsing the chunk headers, or file is corrupted
FPNG_DECODE_FAILED_INVALID_IDAT, // IDAT data length is too small and cannot be valid, file is either corrupted or it's a bug
// fpng_decode_file() specific errors
FPNG_DECODE_FILE_OPEN_FAILED,
FPNG_DECODE_FILE_TOO_LARGE,
FPNG_DECODE_FILE_READ_FAILED,
FPNG_DECODE_FILE_SEEK_FAILED
};
// Fast PNG decoding of files ONLY created by fpng_encode_image_to_memory() or fpng_encode_image_to_file().
// If fpng_get_info() or fpng_decode_memory() returns FPNG_DECODE_NOT_FPNG, you should decode the PNG by falling back to a general purpose decoder.
//
// fpng_get_info() parses the PNG header and iterates through all chunks to determine if it's a file written by FPNG, but does not decompress the actual image data so it's relatively fast.
//
// pImage, image_size: Pointer to PNG image data and its size
// width, height: output image's dimensions
// channels_in_file: will be 3 or 4
//
// Returns FPNG_DECODE_SUCCESS on success, otherwise one of the failure codes above.
// If FPNG_DECODE_NOT_FPNG is returned, you must decompress the file with a general purpose PNG decoder.
// If another error occurs, the file is likely corrupted or invalid, but you can still try to decompress the file with another decoder (which will likely fail).
int fpng_get_info(const void* pImage, uint32_t image_size, uint32_t& width, uint32_t& height, uint32_t& channels_in_file);
// fpng_decode_memory() decompresses 24/32bpp PNG files ONLY encoded by this module.
// If the image was written by FPNG, it will decompress the image data, otherwise it will return FPNG_DECODE_NOT_FPNG in which case you should fall back to a general purpose PNG decoder (lodepng, stb_image, libpng, etc.)
//
// pImage, image_size: Pointer to PNG image data and its size
// out: Output 24/32bpp image buffer
// width, height: output image's dimensions
// channels_in_file: will be 3 or 4
// desired_channels: must be 3 or 4
//
// If the image is 24bpp and 32bpp is requested, the alpha values will be set to 0xFF.
// If the image is 32bpp and 24bpp is requested, the alpha values will be discarded.
//
// Returns FPNG_DECODE_SUCCESS on success, otherwise one of the failure codes above.
// If FPNG_DECODE_NOT_FPNG is returned, you must decompress the file with a general purpose PNG decoder.
// If another error occurs, the file is likely corrupted or invalid, but you can still try to decompress the file with another decoder (which will likely fail).
int fpng_decode_memory(const void* pImage, uint32_t image_size, std::vector<uint8_t>& out, uint32_t& width, uint32_t& height, uint32_t& channels_in_file, uint32_t desired_channels);
#ifndef FPNG_NO_STDIO
int fpng_decode_file(const char* pFilename, std::vector<uint8_t>& out, uint32_t& width, uint32_t& height, uint32_t& channels_in_file, uint32_t desired_channels);
#endif
// ---- Internal API used for Huffman table training purposes
#if FPNG_TRAIN_HUFFMAN_TABLES
const uint32_t HUFF_COUNTS_SIZE = 288;
extern uint64_t g_huff_counts[HUFF_COUNTS_SIZE];
bool create_dynamic_block_prefix(uint64_t* pFreq, uint32_t num_chans, std::vector<uint8_t>& prefix, uint64_t& bit_buf, int& bit_buf_size, uint32_t *pCodes, uint8_t *pCodesizes);
#endif
} // namespace fpng

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Pranav
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,283 @@
<p align="center">
<img height="90" src="img/logo.png"/>
</p>
<p align="center">
Unix-style pathname pattern expansion
</p>
## Table of Contents
- [Quick Start](#quick-start)
* [Build Library and Standalone Sample](#build-library-and-standalone-sample)
- [Usage](#usage)
- [API](#api)
- [Wildcards](#wildcards)
- [Examples](#examples)
* [Match file extensions](#match-file-extensions)
* [Match files in absolute pathnames](#match-files-in-absolute-pathnames)
* [Wildcards: Match a range of characters listed in brackets ('[]')](#wildcards-match-a-range-of-characters-listed-in-brackets-)
* [Exclude files from the matching](#exclude-files-from-the-matching)
* [Wildcards: Match any one character with question mark ('?')](#wildcards-match-any-one-character-with-question-mark-)
* [Case sensitivity](#case-sensitivity)
* [Tilde expansion](#tilde-expansion)
- [Contributing](#contributing)
- [License](#license)
## Quick Start
* This library is available in two flavors:
1. Two file version: `glob.h` and `glob.cpp`
2. Single header file version in `single_include/`
* No external dependencies - just the standard library
* Requires C++17 `std::filesystem`
- If you can't use `C++17`, you can integrate [gulrak/filesystem](https://github.com/gulrak/filesystem) with minimal effort.
* MIT License
### Build Library and Standalone Sample
```bash
cmake -Hall -Bbuild
cmake --build build
# run standalone `glob` sample
./build/standalone/glob --help
```
### Usage
```cpp
// Match on a single pattern
for (auto& p : glob::glob("~/.b*")) { // e.g., .bash_history, .bashrc
// do something with `p`
}
// Match on multiple patterns
for (auto& p : glob::glob({"*.png", "*.jpg"})) { // e.g., foo.png, bar.jpg
// do something with `p`
}
// Match recursively with `rglob`
for (auto& p : glob::rglob("**/*.hpp")) { // e.g., include/foo.hpp, include/foo/bar.hpp
// do something with `p`
}
```
## API
```cpp
/// e.g., glob("*.hpp")
/// e.g., glob("**/*.cpp")
/// e.g., glob("test_files_02/[0-9].txt")
/// e.g., glob("/usr/local/include/nc*.h")
/// e.g., glob("test_files_02/?.txt")
vector<filesystem::path> glob(string pathname);
/// Globs recursively
/// e.g., rglob("Documents/Projects/Foo/**/*.hpp")
/// e.g., rglob("test_files_02/*[0-9].txt")
vector<filesystem::path> rglob(string pathname);
```
There are also two convenience functions to `glob` on a list of patterns:
```cpp
/// e.g., glob({"*.png", "*.jpg"})
vector<filesystem::path> glob(vector<string> pathnames);
/// Globs recursively
/// e.g., rglob({"**/*.h", "**/*.hpp", "**/*.cpp"})
vector<filesystem::path> rglob(vector<string> pathnames);
```
## Wildcards
| Wildcard | Matches | Example
|--- |--- |--- |
| `*` | any characters | `*.txt` matches all files with the txt extension |
| `?` | any one character | `???` matches files with 3 characters long |
| `[]` | any character listed in the brackets | `[ABC]*` matches files starting with A,B or C |
| `[-]` | any character in the range listed in brackets | `[A-Z]*` matches files starting with capital letters |
| `[!]` | any character not listed in the brackets | `[!ABC]*` matches files that do not start with A,B or C |
## Examples
The following examples use the [standalone](standalone/source/main.cpp) sample that is part of this repository to illustrate the library functionality.
```console
foo@bar:~$ ./build/standalone/glob -h
Run glob to find all the pathnames matching a specified pattern
Usage:
./build/standalone/glob [OPTION...]
-h, --help Show help
-v, --version Print the current version number
-r, --recursive Run glob recursively
-i, --input arg Patterns to match
```
### Match file extensions
```console
foo@bar:~$ tree
.
├── include
│   └── foo
│   ├── bar.hpp
│   ├── baz.hpp
│   └── foo.hpp
└── test
├── bar.cpp
├── doctest.hpp
├── foo.cpp
└── main.cpp
3 directories, 7 files
foo@bar:~$ ./glob -i "**/*.hpp"
"test/doctest.hpp"
foo@bar:~$ ./glob -i "**/**/*.hpp"
"include/foo/baz.hpp"
"include/foo/foo.hpp"
"include/foo/bar.hpp"
```
***NOTE*** If you run glob recursively, i.e., using `rglob`:
```console
foo@bar:~$ ./glob -r -i "**/*.hpp"
"test/doctest.hpp"
"include/foo/baz.hpp"
"include/foo/foo.hpp"
"include/foo/bar.hpp"
```
### Match files in absolute pathnames
```console
foo@bar:~$ ./glob -i '/usr/local/include/nc*.h'
"/usr/local/include/ncCheck.h"
"/usr/local/include/ncGroupAtt.h"
"/usr/local/include/ncUshort.h"
"/usr/local/include/ncByte.h"
"/usr/local/include/ncString.h"
"/usr/local/include/ncUint64.h"
"/usr/local/include/ncGroup.h"
"/usr/local/include/ncUbyte.h"
"/usr/local/include/ncvalues.h"
"/usr/local/include/ncInt.h"
"/usr/local/include/ncAtt.h"
"/usr/local/include/ncVar.h"
"/usr/local/include/ncUint.h"
```
### Wildcards: Match a range of characters listed in brackets ('[]')
```console
foo@bar:~$ ls test_files_02
1.txt 2.txt 3.txt 4.txt
foo@bar:~$ ./glob -i 'test_files_02/[0-9].txt'
"test_files_02/4.txt"
"test_files_02/3.txt"
"test_files_02/2.txt"
"test_files_02/1.txt"
foo@bar:~$ ./glob -i 'test_files_02/[1-2]*'
"test_files_02/2.txt"
"test_files_02/1.txt"
```
```console
foo@bar:~$ ls test_files_03
file1.txt file2.txt file3.txt file4.txt
foo@bar:~$ ./glob -i 'test_files_03/file[0-9].*'
"test_files_03/file2.txt"
"test_files_03/file3.txt"
"test_files_03/file1.txt"
"test_files_03/file4.txt"
```
### Exclude files from the matching
```console
foo@bar:~$ ls test_files_01
__init__.py bar.py foo.py
foo@bar:~$ ./glob -i 'test_files_01/*[!__init__].py'
"test_files_01/bar.py"
"test_files_01/foo.py"
foo@bar:~$ ./glob -i 'test_files_01/*[!__init__][!bar].py'
"test_files_01/foo.py"
foo@bar:~$ ./glob -i 'test_files_01/[!_]*.py'
"test_files_01/bar.py"
"test_files_01/foo.py"
```
### Wildcards: Match any one character with question mark ('?')
```console
foo@bar:~$ ls test_files_02
1.txt 2.txt 3.txt 4.txt
foo@bar:~$ ./glob -i 'test_files_02/?.txt'
"test_files_02/4.txt"
"test_files_02/3.txt"
"test_files_02/2.txt"
"test_files_02/1.txt"
```
```console
foo@bar:~$ ls test_files_03
file1.txt file2.txt file3.txt file4.txt
foo@bar:~$ ./glob -i 'test_files_03/????[3-4].txt'
"test_files_03/file3.txt"
"test_files_03/file4.txt"
```
### Case sensitivity
`glob` matching is case-sensitive:
```console
foo@bar:~$ ls test_files_05
file1.png file2.png file3.PNG file4.PNG
foo@bar:~$ ./glob -i 'test_files_05/*.png'
"test_files_05/file2.png"
"test_files_05/file1.png"
foo@bar:~$ ./glob -i 'test_files_05/*.PNG'
"test_files_05/file3.PNG"
"test_files_05/file4.PNG"
foo@bar:~$ ./glob -i "test_files_05/*.png","test_files_05/*.PNG"
"test_files_05/file2.png"
"test_files_05/file1.png"
"test_files_05/file3.PNG"
"test_files_05/file4.PNG"
```
### Tilde expansion
```console
foo@bar:~$ ./glob -i "~/.b*"
"/Users/pranav/.bashrc"
"/Users/pranav/.bash_sessions"
"/Users/pranav/.bash_profile"
"/Users/pranav/.bash_history"
foo@bar:~$ ./glob -i "~/Documents/Projects/glob/**/glob/*.h"
"/Users/pranav/Documents/Projects/glob/include/glob/glob.h"
```
## Contributing
Contributions are welcome, have a look at the [CONTRIBUTING.md](CONTRIBUTING.md) document for more information.
## License
The project is available under the [MIT](https://opensource.org/licenses/MIT) license.

View File

@ -0,0 +1,40 @@
#pragma once
#include <filesystem>
#include <string>
#include <vector>
namespace glob {
/// \param pathname string containing a path specification
/// \return vector of paths that match the pathname
///
/// Pathnames can be absolute (/usr/src/Foo/Makefile) or relative (../../Tools/*/*.gif)
/// Pathnames can contain shell-style wildcards
/// Broken symlinks are included in the results (as in the shell)
std::vector<std::filesystem::path> glob(const std::string &pathname);
/// \param pathnames string containing a path specification
/// \return vector of paths that match the pathname
///
/// Globs recursively.
/// The pattern “**” will match any files and zero or more directories, subdirectories and
/// symbolic links to directories.
std::vector<std::filesystem::path> rglob(const std::string &pathname);
/// Runs `glob` against each pathname in `pathnames` and accumulates the results
std::vector<std::filesystem::path> glob(const std::vector<std::string> &pathnames);
/// Runs `rglob` against each pathname in `pathnames` and accumulates the results
std::vector<std::filesystem::path> rglob(const std::vector<std::string> &pathnames);
/// Initializer list overload for convenience
std::vector<std::filesystem::path> glob(const std::initializer_list<std::string> &pathnames);
/// Initializer list overload for convenience
std::vector<std::filesystem::path> rglob(const std::initializer_list<std::string> &pathnames);
/// Returns true if the input path matche the glob pattern
bool fnmatch(const std::filesystem::path &name, const std::string &pattern);
} // namespace glob

View File

@ -0,0 +1,448 @@
//
// TinyUSDZ modification:
// - Disable exception
// - Use GHC filesystem
//
#pragma once
#include <cassert>
// Assume ghc filesystem header is included(with NO_EXCEPTION version)
//#include <filesystem>
#include <functional>
#include <iostream>
#include <map>
#include <regex>
#include <string>
#include <system_error>
#include <vector>
#if 0
namespace fs = std::filesystem;
#else
namespace fs = ghc::filesystem;
#endif
namespace glob {
namespace {
static inline
bool string_replace(std::string &str, const std::string &from, const std::string &to) {
std::size_t start_pos = str.find(from);
if (start_pos == std::string::npos)
return false;
str.replace(start_pos, from.length(), to);
return true;
}
static inline
std::string translate(const std::string &pattern) {
std::size_t i = 0, n = pattern.size();
std::string result_string;
while (i < n) {
auto c = pattern[i];
i += 1;
if (c == '*') {
result_string += ".*";
} else if (c == '?') {
result_string += ".";
} else if (c == '[') {
auto j = i;
if (j < n && pattern[j] == '!') {
j += 1;
}
if (j < n && pattern[j] == ']') {
j += 1;
}
while (j < n && pattern[j] != ']') {
j += 1;
}
if (j >= n) {
result_string += "\\[";
} else {
auto stuff = std::string(pattern.begin() + i, pattern.begin() + j);
if (stuff.find("--") == std::string::npos) {
string_replace(stuff, std::string{"\\"}, std::string{R"(\\)"});
} else {
std::vector<std::string> chunks;
std::size_t k = 0;
if (pattern[i] == '!') {
k = i + 2;
} else {
k = i + 1;
}
while (true) {
k = pattern.find("-", k, j);
if (k == std::string::npos) {
break;
}
chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + k));
i = k + 1;
k = k + 3;
}
chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + j));
// Escape backslashes and hyphens for set difference (--).
// Hyphens that create ranges shouldn't be escaped.
bool first = false;
for (auto &s : chunks) {
string_replace(s, std::string{"\\"}, std::string{R"(\\)"});
string_replace(s, std::string{"-"}, std::string{R"(\-)"});
if (first) {
stuff += s;
first = false;
} else {
stuff += "-" + s;
}
}
}
// Escape set operations (&&, ~~ and ||).
std::string result;
std::regex_replace(std::back_inserter(result), // result
stuff.begin(), stuff.end(), // string
std::regex(std::string{R"([&~|])"}), // pattern
std::string{R"(\\\1)"}); // repl
stuff = result;
i = j + 1;
if (stuff[0] == '!') {
stuff = "^" + std::string(stuff.begin() + 1, stuff.end());
} else if (stuff[0] == '^' || stuff[0] == '[') {
stuff = "\\\\" + stuff;
}
result_string = result_string + "[" + stuff + "]";
}
} else {
// SPECIAL_CHARS
// closing ')', '}' and ']'
// '-' (a range in character set)
// '&', '~', (extended character set operations)
// '#' (comment) and WHITESPACE (ignored) in verbose mode
static std::string special_characters = "()[]{}?*+-|^$\\.&~# \t\n\r\v\f";
static std::map<int, std::string> special_characters_map;
if (special_characters_map.empty()) {
for (auto &sc : special_characters) {
special_characters_map.insert(
std::make_pair(static_cast<int>(sc), std::string{"\\"} + std::string(1, sc)));
}
}
if (special_characters.find(c) != std::string::npos) {
result_string += special_characters_map[static_cast<int>(c)];
} else {
result_string += c;
}
}
}
return std::string{"(("} + result_string + std::string{R"()|[\r\n])$)"};
}
static inline
std::regex compile_pattern(const std::string &pattern) {
return std::regex(translate(pattern), std::regex::ECMAScript);
}
static inline
bool fnmatch(const fs::path &name, const std::string &pattern) {
return std::regex_match(name.string(), compile_pattern(pattern));
}
static inline
std::vector<fs::path> filter(const std::vector<fs::path> &names,
const std::string &pattern) {
// std::cout << "Pattern: " << pattern << "\n";
std::vector<fs::path> result;
for (auto &name : names) {
// std::cout << "Checking for " << name.string() << "\n";
if (fnmatch(name, pattern)) {
result.push_back(name);
}
}
return result;
}
static inline
fs::path expand_tilde(fs::path path) {
if (path.empty()) return path;
#ifdef _WIN32
char* home;
size_t sz;
_dupenv_s(&home, &sz, "USERPROFILE");
#else
const char * home = std::getenv("HOME");
#endif
if (home == nullptr) {
//throw std::invalid_argument("error: Unable to expand `~` - HOME environment variable not set.");
return path;
}
std::string s = path.string();
if (s[0] == '~') {
s = std::string(home) + s.substr(1, s.size() - 1);
return fs::path(s);
} else {
return path;
}
}
static inline
bool has_magic(const std::string &pathname) {
static const auto magic_check = std::regex("([*?[])");
return std::regex_search(pathname, magic_check);
}
static inline
bool is_hidden(const std::string &pathname) {
return std::regex_match(pathname, std::regex("^(.*\\/)*\\.[^\\.\\/]+\\/*$"));
}
static inline
bool is_recursive(const std::string &pattern) { return pattern == "**"; }
static inline
std::vector<fs::path> iter_directory(const fs::path &dirname, bool dironly) {
std::vector<fs::path> result;
std::error_code ec;
auto current_directory = dirname;
if (current_directory.empty()) {
current_directory = fs::current_path(ec);
}
if (fs::exists(current_directory, ec)) {
#if 0
try {
for (auto &entry : fs::directory_iterator(
current_directory, fs::directory_options::follow_directory_symlink |
fs::directory_options::skip_permission_denied, ec)) {
if (!dironly || entry.is_directory()) {
if (dirname.is_absolute()) {
result.push_back(entry.path());
} else {
result.push_back(fs::relative(entry.path()));
}
}
}
} catch (std::exception&) {
// not a directory
// do nothing
}
#else
auto it = fs::directory_iterator(current_directory, ec);
auto itE = fs::end(it);
for (; it != itE; it.increment(ec)) {
if (ec) {
// TODO: Report error
continue;
}
if (!dironly || it->is_directory(ec)) {
if (dirname.is_absolute()) {
result.push_back(it->path());
} else {
result.push_back(fs::relative(it->path(), ec));
}
}
}
#endif
}
return result;
}
// Recursively yields relative pathnames inside a literal directory.
static inline
std::vector<fs::path> rlistdir(const fs::path &dirname, bool dironly) {
std::vector<fs::path> result;
auto names = iter_directory(dirname, dironly);
for (auto &x : names) {
if (!is_hidden(x.string())) {
result.push_back(x);
for (auto &y : rlistdir(x, dironly)) {
result.push_back(y);
}
}
}
return result;
}
// This helper function recursively yields relative pathnames inside a literal
// directory.
static inline
std::vector<fs::path> glob2(const fs::path &dirname, const std::string &pattern,
bool dironly) {
(void)pattern;
// std::cout << "In glob2\n";
std::vector<fs::path> result;
assert(is_recursive(pattern));
for (auto &dir : rlistdir(dirname, dironly)) {
result.push_back(dir);
}
return result;
}
// These 2 helper functions non-recursively glob inside a literal directory.
// They return a list of basenames. _glob1 accepts a pattern while _glob0
// takes a literal basename (so it only has to check for its existence).
static inline
std::vector<fs::path> glob1(const fs::path &dirname, const std::string &pattern,
bool dironly) {
// std::cout << "In glob1\n";
auto names = iter_directory(dirname, dironly);
std::vector<fs::path> filtered_names;
for (auto &n : names) {
if (!is_hidden(n.string())) {
filtered_names.push_back(n.filename());
// if (n.is_relative()) {
// // std::cout << "Filtered (Relative): " << n << "\n";
// filtered_names.push_back(fs::relative(n));
// } else {
// // std::cout << "Filtered (Absolute): " << n << "\n";
// filtered_names.push_back(n.filename());
// }
}
}
return filter(filtered_names, pattern);
}
static inline
std::vector<fs::path> glob0(const fs::path &dirname, const fs::path &basename,
bool /*dironly*/) {
// std::cout << "In glob0\n";
std::vector<fs::path> result;
std::error_code ec;
if (basename.empty()) {
// 'q*x/' should match only directories.
if (fs::is_directory(dirname, ec)) {
result = {basename};
}
} else {
if (fs::exists(dirname / basename, ec)) {
result = {basename};
}
}
return result;
}
static inline
std::vector<fs::path> glob(const std::string &pathname, bool recursive = false,
bool dironly = false) {
std::vector<fs::path> result;
auto path = fs::path(pathname);
std::error_code ec;
if (pathname[0] == '~') {
// expand tilde
path = expand_tilde(path);
}
auto dirname = path.parent_path();
const auto basename = path.filename();
if (!has_magic(pathname)) {
assert(!dironly);
if (!basename.empty()) {
if (fs::exists(path, ec)) {
result.push_back(path);
}
} else {
// Patterns ending with a slash should match only directories
if (fs::is_directory(dirname, ec)) {
result.push_back(path);
}
}
return result;
}
if (dirname.empty()) {
if (recursive && is_recursive(basename.string())) {
return glob2(dirname, basename.string(), dironly);
} else {
return glob1(dirname, basename.string(), dironly);
}
}
std::vector<fs::path> dirs;
if (dirname != fs::path(pathname) && has_magic(dirname.string())) {
dirs = glob(dirname.string(), recursive, true);
} else {
dirs = {dirname};
}
std::function<std::vector<fs::path>(const fs::path &, const std::string &, bool)>
glob_in_dir;
if (has_magic(basename.string())) {
if (recursive && is_recursive(basename.string())) {
glob_in_dir = glob2;
} else {
glob_in_dir = glob1;
}
} else {
glob_in_dir = glob0;
}
for (auto &d : dirs) {
for (auto &name : glob_in_dir(d, basename.string(), dironly)) {
fs::path subresult = name;
if (name.parent_path().empty()) {
subresult = d / name;
}
result.push_back(subresult);
}
}
return result;
}
} // namespace end
static inline
std::vector<fs::path> glob(const std::string &pathname) {
return glob(pathname, false);
}
static inline
std::vector<fs::path> rglob(const std::string &pathname) {
return glob(pathname, true);
}
static inline
std::vector<fs::path> glob(const std::vector<std::string> &pathnames) {
std::vector<fs::path> result;
for (auto &pathname : pathnames) {
for (auto &match : glob(pathname, false)) {
result.push_back(std::move(match));
}
}
return result;
}
static inline
std::vector<fs::path> rglob(const std::vector<std::string> &pathnames) {
std::vector<fs::path> result;
for (auto &pathname : pathnames) {
for (auto &match : glob(pathname, true)) {
result.push_back(std::move(match));
}
}
return result;
}
static inline
std::vector<fs::path>
glob(const std::initializer_list<std::string> &pathnames) {
return glob(std::vector<std::string>(pathnames));
}
static inline
std::vector<fs::path>
rglob(const std::initializer_list<std::string> &pathnames) {
return rglob(std::vector<std::string>(pathnames));
}
} // namespace glob

View File

@ -0,0 +1,381 @@
#include <cassert>
#include <functional>
#include <glob/glob.h>
#include <iostream>
#include <map>
#include <regex>
namespace fs = std::filesystem;
namespace glob {
namespace {
bool string_replace(std::string &str, const std::string &from, const std::string &to) {
std::size_t start_pos = str.find(from);
if (start_pos == std::string::npos)
return false;
str.replace(start_pos, from.length(), to);
return true;
}
std::string translate(const std::string &pattern) {
std::size_t i = 0, n = pattern.size();
std::string result_string;
while (i < n) {
auto c = pattern[i];
i += 1;
if (c == '*') {
result_string += ".*";
} else if (c == '?') {
result_string += ".";
} else if (c == '[') {
auto j = i;
if (j < n && pattern[j] == '!') {
j += 1;
}
if (j < n && pattern[j] == ']') {
j += 1;
}
while (j < n && pattern[j] != ']') {
j += 1;
}
if (j >= n) {
result_string += "\\[";
} else {
auto stuff = std::string(pattern.begin() + i, pattern.begin() + j);
if (stuff.find("--") == std::string::npos) {
string_replace(stuff, std::string{"\\"}, std::string{R"(\\)"});
} else {
std::vector<std::string> chunks;
std::size_t k = 0;
if (pattern[i] == '!') {
k = i + 2;
} else {
k = i + 1;
}
while (true) {
k = pattern.find("-", k, j);
if (k == std::string::npos) {
break;
}
chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + k));
i = k + 1;
k = k + 3;
}
chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + j));
// Escape backslashes and hyphens for set difference (--).
// Hyphens that create ranges shouldn't be escaped.
bool first = true;
for (auto &s : chunks) {
string_replace(s, std::string{"\\"}, std::string{R"(\\)"});
string_replace(s, std::string{"-"}, std::string{R"(\-)"});
if (first) {
stuff += s;
first = false;
} else {
stuff += "-" + s;
}
}
}
// Escape set operations (&&, ~~ and ||).
std::string result;
std::regex_replace(std::back_inserter(result), // ressult
stuff.begin(), stuff.end(), // string
std::regex(std::string{R"([&~|])"}), // pattern
std::string{R"(\\\1)"}); // repl
stuff = result;
i = j + 1;
if (stuff[0] == '!') {
stuff = "^" + std::string(stuff.begin() + 1, stuff.end());
} else if (stuff[0] == '^' || stuff[0] == '[') {
stuff = "\\\\" + stuff;
}
result_string = result_string + "[" + stuff + "]";
}
} else {
// SPECIAL_CHARS
// closing ')', '}' and ']'
// '-' (a range in character set)
// '&', '~', (extended character set operations)
// '#' (comment) and WHITESPACE (ignored) in verbose mode
static std::string special_characters = "()[]{}?*+-|^$\\.&~# \t\n\r\v\f";
static std::map<int, std::string> special_characters_map;
if (special_characters_map.empty()) {
for (auto &sc : special_characters) {
special_characters_map.insert(
std::make_pair(static_cast<int>(sc), std::string{"\\"} + std::string(1, sc)));
}
}
if (special_characters.find(c) != std::string::npos) {
result_string += special_characters_map[static_cast<int>(c)];
} else {
result_string += c;
}
}
}
return std::string{"(("} + result_string + std::string{R"()|[\r\n])$)"};
}
std::regex compile_pattern(const std::string &pattern) {
return std::regex(translate(pattern), std::regex::ECMAScript);
}
bool fnmatch(const fs::path &name, const std::string &pattern) {
return std::regex_match(name.string(), compile_pattern(pattern));
}
std::vector<fs::path> filter(const std::vector<fs::path> &names,
const std::string &pattern) {
// std::cout << "Pattern: " << pattern << "\n";
std::vector<fs::path> result;
for (auto &name : names) {
// std::cout << "Checking for " << name.string() << "\n";
if (fnmatch(name, pattern)) {
result.push_back(name);
}
}
return result;
}
fs::path expand_tilde(fs::path path) {
if (path.empty()) return path;
const char * home = std::getenv("HOME");
if (home == nullptr) {
throw std::invalid_argument("error: Unable to expand `~` - HOME environment variable not set.");
}
std::string s = path.string();
if (s[0] == '~') {
s = std::string(home) + s.substr(1, s.size() - 1);
return fs::path(s);
} else {
return path;
}
}
bool has_magic(const std::string &pathname) {
static const auto magic_check = std::regex("([*?[])");
return std::regex_search(pathname, magic_check);
}
bool is_hidden(const std::string &pathname) { return pathname[0] == '.'; }
bool is_recursive(const std::string &pattern) { return pattern == "**"; }
std::vector<fs::path> iter_directory(const fs::path &dirname, bool dironly) {
std::vector<fs::path> result;
auto current_directory = dirname;
if (current_directory.empty()) {
current_directory = fs::current_path();
}
if (fs::exists(current_directory)) {
try {
for (auto &entry : fs::directory_iterator(
current_directory, fs::directory_options::follow_directory_symlink |
fs::directory_options::skip_permission_denied)) {
if (!dironly || entry.is_directory()) {
if (dirname.is_absolute()) {
result.push_back(entry.path());
} else {
result.push_back(fs::relative(entry.path()));
}
}
}
} catch (std::exception&) {
// not a directory
// do nothing
}
}
return result;
}
// Recursively yields relative pathnames inside a literal directory.
std::vector<fs::path> rlistdir(const fs::path &dirname, bool dironly) {
std::vector<fs::path> result;
auto names = iter_directory(dirname, dironly);
for (auto &x : names) {
if (!is_hidden(x.string())) {
result.push_back(x);
for (auto &y : rlistdir(x, dironly)) {
result.push_back(y);
}
}
}
return result;
}
// This helper function recursively yields relative pathnames inside a literal
// directory.
std::vector<fs::path> glob2(const fs::path &dirname, [[maybe_unused]] const fs::path &pattern,
bool dironly) {
// std::cout << "In glob2\n";
std::vector<fs::path> result;
assert(is_recursive(pattern.string()));
for (auto &dir : rlistdir(dirname, dironly)) {
result.push_back(dir);
}
return result;
}
// These 2 helper functions non-recursively glob inside a literal directory.
// They return a list of basenames. _glob1 accepts a pattern while _glob0
// takes a literal basename (so it only has to check for its existence).
std::vector<fs::path> glob1(const fs::path &dirname, const fs::path &pattern,
bool dironly) {
// std::cout << "In glob1\n";
auto names = iter_directory(dirname, dironly);
std::vector<fs::path> filtered_names;
for (auto &n : names) {
if (!is_hidden(n.string())) {
filtered_names.push_back(n.filename());
// if (n.is_relative()) {
// // std::cout << "Filtered (Relative): " << n << "\n";
// filtered_names.push_back(fs::relative(n));
// } else {
// // std::cout << "Filtered (Absolute): " << n << "\n";
// filtered_names.push_back(n.filename());
// }
}
}
return filter(filtered_names, pattern.string());
}
std::vector<fs::path> glob0(const fs::path &dirname, const fs::path &basename,
bool /*dironly*/) {
// std::cout << "In glob0\n";
std::vector<fs::path> result;
if (basename.empty()) {
// 'q*x/' should match only directories.
if (fs::is_directory(dirname)) {
result = {basename};
}
} else {
if (fs::exists(dirname / basename)) {
result = {basename};
}
}
return result;
}
std::vector<fs::path> glob(const fs::path &inpath, bool recursive = false,
bool dironly = false) {
std::vector<fs::path> result;
const auto pathname = inpath.string();
auto path = fs::path(pathname);
if (pathname[0] == '~') {
// expand tilde
path = expand_tilde(path);
}
auto dirname = path.parent_path();
const auto basename = path.filename();
if (!has_magic(pathname)) {
assert(!dironly);
if (!basename.empty()) {
if (fs::exists(path)) {
result.push_back(path);
}
} else {
// Patterns ending with a slash should match only directories
if (fs::is_directory(dirname)) {
result.push_back(path);
}
}
return result;
}
if (dirname.empty()) {
if (recursive && is_recursive(basename.string())) {
return glob2(dirname, basename, dironly);
} else {
return glob1(dirname, basename, dironly);
}
}
std::vector<fs::path> dirs;
if (dirname != fs::path(pathname) && has_magic(dirname.string())) {
dirs = glob(dirname, recursive, true);
} else {
dirs = {dirname};
}
std::function<std::vector<fs::path>(const fs::path &, const fs::path &, bool)>
glob_in_dir;
if (has_magic(basename.string())) {
if (recursive && is_recursive(basename.string())) {
glob_in_dir = glob2;
} else {
glob_in_dir = glob1;
}
} else {
glob_in_dir = glob0;
}
for (auto &d : dirs) {
for (auto &name : glob_in_dir(d, basename, dironly)) {
fs::path subresult = name;
if (name.parent_path().empty()) {
subresult = d / name;
}
result.push_back(subresult);
}
}
return result;
}
} // namespace end
std::vector<fs::path> glob(const std::string &pathname) {
return glob(pathname, false);
}
std::vector<fs::path> rglob(const std::string &pathname) {
return glob(pathname, true);
}
std::vector<std::filesystem::path> glob(const std::vector<std::string> &pathnames) {
std::vector<std::filesystem::path> result;
for (auto &pathname : pathnames) {
for (auto &match : glob(pathname, false)) {
result.push_back(std::move(match));
}
}
return result;
}
std::vector<std::filesystem::path> rglob(const std::vector<std::string> &pathnames) {
std::vector<std::filesystem::path> result;
for (auto &pathname : pathnames) {
for (auto &match : glob(pathname, true)) {
result.push_back(std::move(match));
}
}
return result;
}
std::vector<std::filesystem::path>
glob(const std::initializer_list<std::string> &pathnames) {
return glob(std::vector<std::string>(pathnames));
}
std::vector<std::filesystem::path>
rglob(const std::initializer_list<std::string> &pathnames) {
return rglob(std::vector<std::string>(pathnames));
}
} // namespace glob

View File

@ -0,0 +1,384 @@
/*
MIT License
Copyright (c) 2019 Syoyo Fujita
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef TINYMESHUTILS_HALF_EDGE_HH_
#define TINYMESHUTILS_HALF_EDGE_HH_
// TODO(syoyo): Support uint64_t for Edge.
#include <algorithm>
#include <cstdint>
#include <vector>
//#include <iostream> // DBG
// Using robin-map may give better performance
#if defined(TINYMESHUTILS_USE_ROBINMAP)
#error TODO
#include <tsl/robin_hash.h>
#else
#include <unordered_map>
// namespace umap = std::unordered_map;
#endif
namespace tinymeshutils {
struct Edge {
Edge(const uint32_t _v0, const uint32_t _v1) : v0(_v0), v1(_v1) {}
// create an 64 bit identifier that is unique for the not oriented edge
operator uint64_t() const {
uint32_t p0 = v0, p1 = v1;
if (p0 < p1) std::swap(p0, p1);
return (uint64_t(p0) << 32) | uint64_t(p1);
}
uint32_t v0, v1; // start, end
};
struct HalfEdge {
// invalid or undefined = -1
int64_t opposite_halfedge{-1}; // index to the halfedges array.
int64_t next_halfedge{-1}; // index to the halfedges array.
// int64_t vertex_index{-1}; // vertex index at the start of the edge
int64_t face_index{-1}; // index to face indices
int64_t edge_index{-1}; // index to edge indices
};
// Functor object for Edge
struct EdgeHash {
size_t operator()(const std::pair<uint32_t, uint32_t> &k) const {
if (sizeof(size_t) == 8) {
// We can create unique value
return (uint64_t(k.first) << 32) | uint64_t(k.second);
} else {
// 32bit environment. we cannot create unique hash value.
return std::hash<uint32_t>()(k.first) ^ std::hash<uint32_t>()(k.second);
}
}
};
///
/// Simple half-edge construction for polygons.
///
/// @param[in] face_vert_indices Array of face vertex indices.
/// @param[in] face_vert_counts Array of the number of vertices for each face(3
/// = triangle, 4 = quad, ...).
/// @param[out] edges Array of edges constructed
/// @param[out] halfedges Array of half-edges comstructed. length =
/// face_vert_indices.size()
/// @param[out] vertex_starting_halfedge_indices Index to starting half-edge in
/// `halfedges` for each vertex. -1 when no half-edge assigned to its vertex.
/// length = the highest value in `face_vert_indices`(= the number of vertices)
/// face_vert_indices.size()
/// @param[out] err Optional error message.
///
/// @return true upon success. Return false when input mesh has invalid
/// topology.
///
/// face_vert_indices.size() is equal to the sum of each elements in
/// `face_vert_counts`. Assume edge is defined in v0 -> v1, v1 -> v2, ... v(N-1)
/// -> v0 order.
///
bool BuildHalfEdge(const std::vector<uint32_t> &face_vert_indices,
const std::vector<uint32_t> &face_vert_counts,
std::vector<Edge> *edges, std::vector<HalfEdge> *halfedges,
std::vector<int64_t> *vertex_starting_halfedge_indices,
std::string *err = nullptr);
} // namespace tinymeshutils
#endif // TINYMESHUTILS_HALF_EDGE_HH_
#if defined(TINYMESHUTILS_HALF_EDGE_IMPLEMENTATION)
namespace tinymeshutils {
bool BuildHalfEdge(const std::vector<uint32_t> &face_vert_indices,
const std::vector<uint32_t> &face_vert_counts,
std::vector<Edge> *edges, std::vector<HalfEdge> *halfedges,
std::vector<int64_t> *vertex_starting_halfedge_indices,
std::string *err) {
// Based on documents at Internet and an implementation
// https://github.com/yig/halfedge
size_t num_indices = 0;
for (size_t i = 0; i < face_vert_counts.size(); i++) {
if (face_vert_counts[i] < 3) {
// invalid # of vertices for a face.
return false;
}
num_indices += face_vert_counts[i];
}
// Find larget number = the number of vertices in input mesh.
uint32_t num_vertices =
(*std::max_element(face_vert_indices.begin(), face_vert_indices.end())) +
1;
std::vector<int64_t> vertex_starting_halfedge_indices_buf(num_vertices, -1);
// allocate buffer for half-edge.
std::vector<HalfEdge> halfedge_buf(num_indices);
std::unordered_map<std::pair<int32_t, int32_t>, size_t, EdgeHash>
halfedge_table; // <<v0, v1>, index to `half_edges`>
//
// 1. Build edges.
//
std::vector<Edge> edge_buf; // linear array of edges.
std::unordered_map<uint64_t, size_t>
edge_map; // <un oriented edge index, index to edge array>
{
std::vector<Edge>
tmp_edges; // linear array of edges. may have duplicated edges
// list up and register edges.
size_t f_offset = 0;
for (size_t f = 0; f < face_vert_counts.size(); f++) {
uint32_t nv = face_vert_counts[f];
if (nv < 3) {
if (err) {
(*err) = "Face " + std::to_string(f) + " has invalid # of vertices " +
std::to_string(nv) + "\n";
}
return false;
}
uint32_t ne = nv;
// for each edge
for (size_t e = 0; e < size_t(ne); e++) {
// std::cout << "e = " << e << ", " << (e + 1) % nv << "\n";
uint32_t v0 = face_vert_indices[f_offset + e];
uint32_t v1 = face_vert_indices[f_offset + (e + 1) % nv];
// std::cout << "v0 = " << v0 << ", v1 = " << v1 << "\n";
tmp_edges.push_back({v0, v1});
}
f_offset += nv;
}
// create edge_map and unique array of edges.
for (const auto &edge : tmp_edges) {
uint64_t key = edge;
if (!edge_map.count(key)) {
size_t edge_idx = edge_buf.size();
edge_map[edge] = edge_idx;
edge_buf.push_back(edge);
}
}
}
#if 0
// dbg
for (size_t i = 0; i < edge_buf.size(); i++) {
std::cout << "edge[" << i << "] = " << edge_buf[i].v0 << ", "
<< edge_buf[i].v1 << "\n";
}
#endif
//
// 2. Register half edges
//
{
size_t f_offset = 0;
for (size_t f = 0; f < face_vert_counts.size(); f++) {
// for each edge
uint32_t nv = face_vert_counts[f];
if (nv < 3) {
if (err) {
(*err) = "Face " + std::to_string(f) + " has invalid # of vertices " +
std::to_string(nv) + "\n";
}
return false;
}
uint32_t ne = nv;
// register
for (size_t e = 0; e < size_t(ne); e++) {
uint32_t v0 = face_vert_indices[f_offset + e];
uint32_t v1 = face_vert_indices[f_offset + (e + 1) % nv];
Edge edge(v0, v1);
// vertex pair must be unique over the input mesh
if (halfedge_table.count(std::make_pair(v0, v1))) {
if (err) {
(*err) = "(Register half edges). Invalid topology. Edge (v0: " +
std::to_string(v0) + ", v1: " + std::to_string(v1) +
") must be unique but duplicated one exists for Face " +
std::to_string(f) + "\n";
}
return false;
}
uint64_t eid = edge; // non oriented
if (!edge_map.count(eid)) {
if (err) {
(*err) = "??? Edge (v0: " + std::to_string(edge.v0) +
", v1: " + std::to_string(edge.v1) +
") does not found for Face " + std::to_string(f) +
". This should not be happen.\n";
}
return false;
}
size_t edge_index = edge_map[eid];
size_t halfedge_offset = f_offset + e;
halfedge_table[std::make_pair(v0, v1)] = halfedge_offset;
halfedge_buf[halfedge_offset].edge_index = int64_t(edge_index);
halfedge_buf[halfedge_offset].face_index = int64_t(f);
halfedge_buf[halfedge_offset].next_halfedge =
int64_t(f_offset + (e + 1) % nv);
if (size_t(v0) >= vertex_starting_halfedge_indices_buf.size()) {
if (err) {
(*err) =
"Out-of-bounds access. v0 " + std::to_string(v0) +
" must be less than " +
std::to_string(vertex_starting_halfedge_indices_buf.size()) +
"\n";
}
return false;
}
if (vertex_starting_halfedge_indices_buf[size_t(v0)] == -1) {
// Set as starting half-edge
vertex_starting_halfedge_indices_buf[size_t(v0)] =
int64_t(halfedge_offset);
}
}
f_offset += nv;
}
}
// dbg
// for (size_t i = 0; i < halfedge_buf.size(); i++) {
// std::cout << "halfedge_buf[" << i << "].edge_index = " <<
// halfedge_buf[i].edge_index << "\n";
//}
//
// 3. Find opposite half edges
//
for (size_t i = 0; i < halfedge_buf.size(); i++) {
HalfEdge &halfedge = halfedge_buf[i];
if ((halfedge.edge_index == -1) ||
(halfedge.edge_index >= int64_t(edge_buf.size()))) {
if (err) {
(*err) = "Invalid edge_index " + std::to_string(halfedge.edge_index) +
". Must be >= 0 and < " + std::to_string(edge_buf.size()) +
"\n";
}
return false;
}
const Edge &edge = edge_buf[size_t(halfedge.edge_index)];
if (halfedge_table.count(std::make_pair(edge.v1, edge.v0)) &&
halfedge_table.count(std::make_pair(edge.v0, edge.v1))) {
// Opposite halfedge exists. Make a link.
size_t halfedge_index0 =
halfedge_table.at(std::make_pair(edge.v0, edge.v1));
size_t halfedge_index1 =
halfedge_table.at(std::make_pair(edge.v1, edge.v0));
if (halfedge_index0 == halfedge_index1) {
if (err) {
(*err) = "Invalid halfedge_index. Both indices has same value.\n";
}
return false;
}
// Check if self-referencing. Choose different index compared to current
// index(`i`).
size_t opposite_halfedge_index =
(halfedge_index0 == i) ? halfedge_index1 : halfedge_index0;
if (opposite_halfedge_index >= halfedge_buf.size()) {
if (err) {
(*err) = "Invalid halfedge_index " +
std::to_string(opposite_halfedge_index) + ". Must be < " +
std::to_string(halfedge_buf.size()) + "\n";
}
return false;
}
HalfEdge &opposite_halfedge = halfedge_buf[opposite_halfedge_index];
if (opposite_halfedge.edge_index != halfedge.edge_index) {
if (err) {
(*err) = "Edge id mismatch. opposite_halfedge.edge_index " +
std::to_string(opposite_halfedge.edge_index) +
" must be equal to halfedge.edge_index " +
std::to_string(halfedge.edge_index) + "\n";
}
return false;
}
// Make a link.
halfedge.opposite_halfedge = int64_t(opposite_halfedge_index);
}
}
(*edges) = edge_buf;
(*halfedges) = halfedge_buf;
(*vertex_starting_halfedge_indices) = vertex_starting_halfedge_indices_buf;
#if 0
// dbg
std::cout << "halfedge_buf.size = " << halfedge_buf.size() << "\n";
for (size_t i = 0; i < halfedge_buf.size(); i++) {
std::cout << "halfedge[" << i << "]. face = " << halfedge_buf[i].face_index
<< ", edge = " << halfedge_buf[i].edge_index
<< ", opposite he = " << halfedge_buf[i].opposite_halfedge
<< ", next he = " << halfedge_buf[i].next_halfedge << "\n";
}
for (size_t i = 0; i < vertex_starting_halfedge_indices_buf.size(); i++) {
std::cout << "v[" << i << "].halfedge_index = " << vertex_starting_halfedge_indices_buf[i] << "\n";
}
#endif
return true;
}
} // namespace tinymeshutils
#endif // TINYMESHUTILS_HALF_EDGE_IMPLEMENTATION

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 ArangoDB GmbH
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.

View File

@ -0,0 +1,196 @@
String to integer conversion library
------------------------------------
This is a header-only library for converting a string containing a
base-10 number into an integer value of a configurable type T.
The library offers functions to parse a string into an integer with
validation of the input string or without validation. It is up to the
embedder to pick the appropriate function.
If the validating functions are used, the input string is considered
valid only if it consists of the digits '0' to '9'. An optional '+'
or '-' sign is allowed at the very beginning of the input string too.
If any other character is found, the input is considered invalid.
The input string does not need to be null-terminated.
If the parsed number value would be less or greater than what the
number type T can store without truncation, the input is considered
invalid and parsing is stopped.
The non-validating functions do not validate the input string for
validity nor do they check for overflow or underflow of the result
value.
The library makes a few assumptions about the input in order to provide
good performance:
* all input is treated as base-10 numbers - no support for hexadecimal
or octal numbers nor for floating point values
* the library functions are optimized for valid input, i.e. strings that
contain only the digits '0' to '9' (with an optional '+' or '-' sign in
front). This is also true for the validating functions
* the library will not handle leading whitespace in the input string.
input strings with leading or trailing whitespace are simply considered
invalid. The same is true for input strings containing non-integer
numbers
* the library functions will not modify `errno` in any way, nor will they
throw any exceptions
* the library functions will not allocate any memory on the heap
In contrast to other common string-to-integer functions, the functions
of this library do not require null-terminated input strings. An input
string is delimited simply by a start pointer (`char const* p`) and an end
pointer (`char const* e`) into its data. All library functions guarantee
to only read memory between `p` (inclusive) and `e` (exclusive).
Use cases
---------
This library's string-to-integer conversion functionality is not as flexible
as the one provided by other string-to-integer functions, e.g. `std::stoull`
from the standard library or `std::strtoull`.
This library sacrifices some of the generality for performance. It is also
optimized for valid input strings, and provides special functions that do not
validate the input at all. Embedders can use these functions when they know
the input strings are valid and will not overflow the target datatype.
Example usage
-------------
```cpp
#include "jsteemann/atoi.h"
#include <iostream>
// the string to be parsed
std::string value("12345678901234");
bool valid;
auto result = jsteemann::atoi<uint64_t>(value.data(), value.data() + value.size(), valid);
if (valid) {
// parsing succeeded!
std::cout << "successfully parsed '" << value << "' into number " << result << std::endl;
} else {
// parsing failed!
std::cout << "failed to parse '" << value << "' into a number!" << std::endl;
}
```
The library contains the following validating functions:
```cpp
// function to convert the string value between p
// (inclusive) and e (exclusive) into a number value of type T
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'. an
// optional '+' or '-' sign is allowed too.
// if any other character is found, the output parameter "valid" will
// be set to false. if the parsed value is less or greater than what
// type T can store without truncation, "valid" will also be set to
// false. In this case the returned result should not be used.
// this function will not modify errno.
template<typename T>
static inline T atoi(char const* p, char const* e, bool& valid) noexcept;
// low-level worker function to convert the string value between p
// (inclusive) and e (exclusive) into a positive number value of type T
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'.
// if any other character is found, the output parameter "valid" will
// be set to false. if the parsed value is greater than what type T can
// store without truncation, "valid" will also be set to false. In this
// case the returned result should not be used.
// this function will not modify errno.
template<typename T>
static inline T atoi_positive(char const* p, char const* e, bool& valid) noexcept;
```
The library contains the following non-validating functions:
```cpp
// function to convert the string value between p
// (inclusive) and e (exclusive) into a number value of type T, without
// validation of the input string - use this only for trusted input!
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'. an
// optional '+' or '-' sign is allowed too.
// there is no validation of the input string, and overflow or underflow
// of the result value will not be detected.
// this function will not modify errno.
template<typename T>
inline T atoi_unchecked(char const* p, char const* e) noexcept;
// low-level worker function to convert the string value between p
// (inclusive) and e (exclusive) into a positive number value of type T,
// without validation of the input string - use this only for trusted input!
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'.
// there is no validation of the input string, and overflow or underflow
// of the result value will not be detected.
// this function will not modify errno.
template<typename T>
inline T atoi_positive_unchecked(char const* p, char const* e) noexcept;
```
Benchmark
---------
To compare the performance of this library and the standard library's
`std::stoull` and `std::strtoull` functions, there is a benchmark executable
included.
It can be built and run as follows:
```bash
mkdir -p build
# be sure to build in Release mode here for compiler optimizations
(cd build && cmake -DCMAKE_BUILD_TYPE=Release ..)
build/benchmark/bench
```
Benchmark results from local laptop (Linux x86-64):
```
500000000 iterations of std::stoull, string '7' took 4792 ms
500000000 iterations of std::strtoull, string '7' took 4482 ms
500000000 iterations of jsteemann::atoi, string '7' took 1027 ms
500000000 iterations of jsteemann::atoi_positive, string '7' took 870 ms
500000000 iterations of jsteemann::atoi_positive_unchecked, string '7' took 873 ms
500000000 iterations of std::stoull, string '874' took 6495 ms
500000000 iterations of std::strtoull, string '874' took 6241 ms
500000000 iterations of jsteemann::atoi, string '874' took 2268 ms
500000000 iterations of jsteemann::atoi_positive, string '874' took 2222 ms
500000000 iterations of jsteemann::atoi_positive_unchecked, string '874' took 1092 ms
500000000 iterations of std::stoull, string '123456' took 9172 ms
500000000 iterations of std::strtoull, string '123456' took 8887 ms
500000000 iterations of jsteemann::atoi, string '123456' took 3945 ms
500000000 iterations of jsteemann::atoi_positive, string '123456' took 3883 ms
500000000 iterations of jsteemann::atoi_positive_unchecked, string '123456' took 1956 ms
500000000 iterations of std::stoull, string '12345654666646' took 16413 ms
500000000 iterations of std::strtoull, string '12345654666646' took 16026 ms
500000000 iterations of jsteemann::atoi, string '12345654666646' took 9061 ms
500000000 iterations of jsteemann::atoi_positive, string '12345654666646' took 8527 ms
500000000 iterations of jsteemann::atoi_positive_unchecked, string '12345654666646' took 4154 ms
500000000 iterations of std::stoull, string '16323949897939569634' took 21772 ms
500000000 iterations of std::strtoull, string '16323949897939569634' took 21537 ms
500000000 iterations of jsteemann::atoi, string '16323949897939569634' took 16677 ms
500000000 iterations of jsteemann::atoi_positive, string '16323949897939569634' took 15597 ms
500000000 iterations of jsteemann::atoi_positive_unchecked, string '16323949897939569634' took 6203 ms
```
Tests
-----
To run the library's tests locally, execute the following commands:
```bash
mkdir -p build
(cd build && cmake ..)
build/tests/tests
```

View File

@ -0,0 +1,250 @@
// Based on https://github.com/jsteemann/atoi
// Modified to return error codes
#ifndef JSTEEMANN_ATOI_H
#define JSTEEMANN_ATOI_H 1
#pragma once
#include <limits>
#include <type_traits>
// some macros to help with branch prediction
#if defined(__GNUC__) || defined(__GNUG__)
#define ATOI_LIKELY(v) __builtin_expect(!!(v), 1)
#define ATOI_UNLIKELY(v) __builtin_expect(!!(v), 0)
#else
#define ATOI_LIKELY(v) v
#define ATOI_UNLIKELY(v) v
#endif
#define JSTEEMANN_NOEXCEPT noexcept
namespace jsteemann {
enum ErrCode : int {
SUCCESS,
INVALID_INPUT = -1,
INVALID_NEGATIVE_SIGN = -2,
VALUE_OVERFLOW = -3,
VALUE_UNDERFLOW= -4
};
//
// errcode
// 0 : success
// -1 : invalid input
// -2 : negative sign(`-`) detected for positive atoi
// -3 : overflow
// -4 : underflow
//
// low-level worker function to convert the string value between p
// (inclusive) and e (exclusive) into a negative number value of type T,
// without validation of the input string - use this only for trusted input!
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'.
// there is no validation of the input string, and overflow or underflow
// of the result value will not be detected.
// this function will not modify errno.
template<typename T>
inline T atoi_negative_unchecked(char const* p, char const* e) JSTEEMANN_NOEXCEPT {
T result = 0;
while (p != e) {
result = (result << 1) + (result << 3) - (*(p++) - '0');
}
return result;
}
// low-level worker function to convert the string value between p
// (inclusive) and e (exclusive) into a positive number value of type T,
// without validation of the input string - use this only for trusted input!
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'.
// there is no validation of the input string, and overflow or underflow
// of the result value will not be detected.
// this function will not modify errno.
template<typename T>
inline T atoi_positive_unchecked(char const* p, char const* e) JSTEEMANN_NOEXCEPT {
T result = 0;
while (p != e) {
result = (result << 1) + (result << 3) + *(p++) - '0';
}
return result;
}
// function to convert the string value between p
// (inclusive) and e (exclusive) into a number value of type T, without
// validation of the input string - use this only for trusted input!
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'. an
// optional '+' or '-' sign is allowed too.
// there is no validation of the input string, and overflow or underflow
// of the result value will not be detected.
// this function will not modify errno.
template<typename T>
inline T atoi_unchecked(char const* p, char const* e) JSTEEMANN_NOEXCEPT {
if (ATOI_UNLIKELY(p == e)) {
return T();
}
if (*p == '-') {
if (!std::is_signed<T>::value) {
return T();
}
return atoi_negative_unchecked<T>(++p, e);
}
if (ATOI_UNLIKELY(*p == '+')) {
++p;
}
return atoi_positive_unchecked<T>(p, e);
}
// low-level worker function to convert the string value between p
// (inclusive) and e (exclusive) into a negative number value of type T
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'.
// if any other character is found, the output parameter "errcode" will
// be set to -1(invalid input). if the parsed value is less than what type T can
// store without truncation, "errcode" will be set to -4(underflow).
// this function will not modify errno.
template<typename T>
inline T atoi_negative(char const* p, char const* e, int& errcode) JSTEEMANN_NOEXCEPT {
if (ATOI_UNLIKELY(p == e)) {
errcode = -1;
return T();
}
constexpr T cutoff = (std::numeric_limits<T>::min)() / 10;
constexpr char cutlim = -((std::numeric_limits<T>::min)() % 10);
T result = 0;
do {
char c = *p;
if ((c == '\0') || (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\v') || (c == '\f')) {
errcode = 0;
return result;
}
// we expect only '0' to '9'. everything else is unexpected
if (ATOI_UNLIKELY(c < '0' || c > '9')) {
errcode = -1;
return result;
}
c -= '0';
// we expect the bulk of values to not hit the bounds restrictions
if (ATOI_UNLIKELY(result < cutoff || (result == cutoff && c > cutlim))) {
errcode = -4;
return result;
}
result *= 10;
result -= c;
} while (++p < e);
errcode = 0;
return result;
}
// low-level worker function to convert the string value between p
// (inclusive) and e (exclusive) into a positive number value of type T
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'.
// if any other character is found, the output parameter "errcode" will
// be set to -1. if the parsed value is greater than what type T can
// store without truncation, "errcode" will be set to -3(overflow).
// this function will not modify errno.
template<typename T>
inline T atoi_positive(char const* p, char const* e, int& errcode) JSTEEMANN_NOEXCEPT {
if (ATOI_UNLIKELY(p == e)) {
errcode = -1;
return T();
}
constexpr T cutoff = (std::numeric_limits<T>::max)() / 10;
constexpr char cutlim = (std::numeric_limits<T>::max)() % 10;
T result = 0;
do {
char c = *p;
if ((c == '\0') || (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\v') || (c == '\f')) {
errcode = 0;
return result;
}
// we expect only '0' to '9'. everything else is unexpected
if (ATOI_UNLIKELY(c < '0' || c > '9')) {
errcode = -1;
return result;
}
c -= '0';
// we expect the bulk of values to not hit the bounds restrictions
if (ATOI_UNLIKELY(result > cutoff || (result == cutoff && c > cutlim))) {
errcode = -3;
return result;
}
result *= 10;
result += c;
} while (++p < e);
errcode = 0;
return result;
}
// function to convert the string value between p
// (inclusive) and e (exclusive) into a number value of type T
//
// the input string will always be interpreted as a base-10 number.
// expects the input string to contain only the digits '0' to '9'. an
// optional '+' or '-' sign is allowed too.
// if any other character is found, the output parameter "errcode" will
// be set to -1. if the parsed value is less or greater than what
// type T can store without truncation, "errcode" will be set to
// -3(oveerflow)
// this function will not modify errno.
template<typename T>
inline typename std::enable_if<std::is_signed<T>::value, T>::type atoi(char const* p, char const* e, int& errcode) JSTEEMANN_NOEXCEPT {
if (ATOI_UNLIKELY(p == e)) {
errcode = -1;
return T();
}
if (*p == '-') {
return atoi_negative<T>(++p, e, errcode);
}
if (ATOI_UNLIKELY(*p == '+')) {
++p;
}
return atoi_positive<T>(p, e, errcode);
}
template<typename T>
inline typename std::enable_if<std::is_unsigned<T>::value, T>::type atoi(char const* p, char const* e, int &errcode) JSTEEMANN_NOEXCEPT {
if (ATOI_UNLIKELY(p == e)) {
errcode = -1;
return T();
}
if (*p == '-') {
errcode = -2;
return T();
}
if (ATOI_UNLIKELY(*p == '+')) {
++p;
}
return atoi_positive<T>(p, e, errcode);
}
}
#endif

View File

@ -0,0 +1,3 @@
Modification by Syoyo Fujita.
reports overflow error.

View File

@ -0,0 +1,482 @@
# linalg.h
[![Release is 2.2-beta](https://img.shields.io/badge/version-2.2--beta-blue.svg)](http://raw.githubusercontent.com/sgorsten/linalg/v3/linalg.h)
[![License is Unlicense](http://img.shields.io/badge/license-Unlicense-blue.svg?style=flat)](http://unlicense.org/)
[![Travis CI build status](http://travis-ci.org/sgorsten/linalg.svg)](https://travis-ci.org/sgorsten/linalg)
[![Appveyor build status](http://ci.appveyor.com/api/projects/status/l4bfv5omodkajuc9?svg=true)](https://ci.appveyor.com/project/sgorsten/linalg)
[`linalg.h`](/linalg.h) is a [single header](http://github.com/nothings/stb/blob/master/docs/other_libs.md), [public domain](http://unlicense.org/), [short vector math](http://www.reedbeta.com/blog/on-vector-math-libraries/) library for [C++](http://en.cppreference.com/w/). It is inspired by the syntax of popular shading and compute languages and is intended to serve as a lightweight alternative to projects such as [GLM](http://glm.g-truc.net/0.9.7/), [Boost.QVM](https://www.boost.org/doc/libs/1_66_0/libs/qvm/doc/index.html) or [Eigen](http://eigen.tuxfamily.org/) in domains such as computer graphics, computational geometry, and physical simulation. It allows you to easily write programs like the following:
```cpp
#include <linalg.h>
using namespace linalg::aliases;
// Compute the coefficients of the equation of a plane containing points a, b, and c
float4 compute_plane(float3 a, float3 b, float3 c)
{
float3 n = cross(b-a, c-a);
return {n, -dot(n,a)};
}
```
`linalg.h` aims to be:
* **Lightweight**: The library is defined in a single header file which is less than a thousand lines of code.
* **Dependency free**: There are no dependencies beyond a compliant C++11 compiler and a small subset of the standard library.
* **Standards compliant**: Almost all operations are free of undefined behavior and can be evaluated in a `constexpr` context.
* **Generic**: All types and operations are parameterized over scalar type, and can be mixed within expressions. Type promotion rules roughly match the C standard.
* **Consistent**: Named functions and overloaded operators perform the same conceptual operation on all data types for which they are supported.
* **Complete**: There are very few restrictions on which operations may be applied to which data types.
* **Easy to integrate**: The library defines no symbols in the public namespace, and provides a mechanism for defining implicit conversions to external or user-provided data types.
The documentation for `v2.2` is still in progress.
* [Data structures](#data-structures)
* [Vectors](#vectors)
* [Matrices](#matrices)
* [Function listing](#function-listing)
* [Vector algebra](#vector-algebra)
* [Quaternion algebra](#quaternion-algebra)
* [Matrix algebra](#matrix-algebra)
* [Component-wise operations](#component-wise-operations)
* [Reductions](#reductions)
* [Optional features](#optional-features)
* [Type aliases](#type-aliases)
* [`ostream` overloads](#ostream-overloads)
* [User-defined conversions](#user-defined-conversions)
* [Higher order functions](#higher-order-functions)
* [Changes from v2.1](#changes-from-v21)
## Data structures
#### Vectors
`linalg::vec<T,M>` defines a fixed-length vector containing exactly `M` elements of type `T`. Convenience aliases such as `float3`, `float4`, or `int2` are provided in the [`linalg::aliases` namespace](#type-aliases). This data structure can be used to store a wide variety of types of data, including geometric vectors, points, homogeneous coordinates, plane equations, colors, texture coordinates, or any other situation where you need to manipulate a small sequence of numbers. As such, `vec<T,M>` is supported by a set of [algebraic](#vector-algebra) and [component-wise](#component-wise-operations) functions, as well as a set of standard [reductions](#reductions).
`vec<T,M>`:
* is [`DefaultConstructible`](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):
```cpp
float3 v; // v contains 0,0,0
```
* is constructible from `M` elements of type `T`:
```cpp
float3 v {1,2,3}; // v contains 1,2,3
```
* is [`CopyConstructible`](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) and [`CopyAssignable`](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):
```cpp
float3 v {1,2,3}; // v contains 1,2,3
float3 u {v}; // u contains 1,2,3
float3 w; // w contains 0,0,0
w = u; // w contains 1,2,3
```
* is [`EqualityComparable`](https://en.cppreference.com/w/cpp/named_req/EqualityComparable) and [`LessThanComparable`](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):
```cpp
if(v == y) cout << "v and u contain equal elements in the same positions" << endl;
if(v < u) cout << "v precedes u lexicographically" << endl;
```
* is **explicitly** constructible from a single element of type `T`:
```cpp
float3 v = float3{4}; // v contains 4,4,4
```
* is **explicitly** constructible from a `vec<U,M>` of some other type `U`:
```cpp
float3 v {1.1f,2.3f,3.5f}; // v contains 1.1,2.3,3.5
int3 u = int3{v}; // u contains 1,2,3
```
* has fields `x,y,z,w`:
```cpp
float y = point.y; // y contains second element of point
pixel.w = 0.5; // fourth element of pixel set to 0.5
float s = tc.x; // s contains first element of tc
```
* supports indexing:
```cpp
float x = v[0]; // x contains first element of v
v[2] = 5; // third element of v set to 5
```
* supports unary operators `+`, `-`, `!` and `~` in component-wise fashion:
```cpp
auto v = -float{2,3}; // v is float2{-2,-3}
```
* supports binary operators `+`, `-`, `*`, `/`, `%`, `|`, `&`, `^`, `<<` and `>>` in component-wise fashion:
```cpp
auto v = float2{1,1} + float2{2,3}; // v is float2{3,4}
```
* supports binary operators with a scalar on the left or the right:
```cpp
auto v = 2 * float3{1,2,3}; // v is float3{2,4,6}
auto u = float3{1,2,3} + 1; // u is float3{2,3,4}
```
* supports operators `+=`, `-=`, `*=`, `/=`, `%=`, `|=`, `&=`, `^=`, `<<=` and `>>=` with vectors or scalars on the right:
```cpp
float2 v {1,2}; v *= 3; // v is float2{3,6}
```
* supports operations on mixed element types:
```cpp
auto v = float3{1,2,3} + int3{4,5,6}; // v is float3{5,7,9}
```
* supports [range-based for](https://en.cppreference.com/w/cpp/language/range-for):
```cpp
for(auto elem : float3{1,2,3}) cout << elem << ' '; // prints "1 2 3 "
```
* has a flat memory layout:
```cpp
float3 v {1,2,3};
float * p = v.data(); // &v[i] == p+i
p[1] = 4; // v contains 1,4,3
```
#### Matrices
`linalg::mat<T,M,N>` defines a fixed-size matrix containing exactly `M` rows and `N` columns of type `T`, in column-major order. Convenience aliases such as `float4x4` or `double3x3` are provided in the [`linalg::aliases` namespace](#type-aliases). This data structure is supported by a set of [algebraic](#matrix-algebra) functions and [component-wise](#component-wise-operations) functions, as well as a set of standard [reductions](#reductions).
`mat<T,M,N>`:
* is [`DefaultConstructible`](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):
```cpp
float2x2 m; // m contains columns 0,0; 0,0
```
* is constructible from `N` columns of type `vec<T,M>`:
```cpp
float2x2 m {{1,2},{3,4}}; // m contains columns 1,2; 3,4
```
* is constructible from `linalg::identity`:
```cpp
float3x3 m = linalg::identity; // m contains columns 1,0,0; 0,1,0; 0,0,1
```
* is [`CopyConstructible`](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) and [`CopyAssignable`](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):
```cpp
float2x2 m {{1,2},{3,4}}; // m contains columns 1,2; 3,4
float2x2 n {m}; // n contains columns 1,2; 3,4
float2x2 p; // p contains columns 0,0; 0,0
p = n; // p contains columns 1,2; 3,4
```
* is [`EqualityComparable`](https://en.cppreference.com/w/cpp/named_req/EqualityComparable) and [`LessThanComparable`](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):
```cpp
if(m == n) cout << "m and n contain equal elements in the same positions" << endl;
if(m < n) cout << "m precedes n lexicographically when compared in column-major order" << endl;
```
* is **explicitly** constructible from a single element of type `T`:
```cpp
float2x2 m {5}; // m contains columns 5,5; 5,5
```
* is **explicitly** constructible from a `mat<U,M,N>` of some other type `U`:
```cpp
float2x2 m {int2x2{{5,6},{7,8}}}; // m contains columns 5,6; 7,8
```
* supports indexing into *columns*:
```cpp
float2x3 m {{1,2},{3,4},{5,6}}; // m contains columns 1,2; 3,4; 5,6
float2 c = m[0]; // c contains 1,2
m[1] = {7,8}; // m contains columns 1,2; 7,8; 5,6
```
* supports retrieval (but not assignment) of rows:
```cpp
float2x3 m {{1,2},{3,4},{5,6}}; // m contains columns 1,2; 3,4; 5,6
float3 r = m.row(1); // r contains 2,4,6
```
* supports unary operators `+`, `-`, `!` and `~` in component-wise fashion:
```cpp
float2x2 m {{1,2},{3,4}}; // m contains columns 1,2; 3,4
float2x2 n = -m; // n contains columns -1,-2; -3,-4
```
* supports binary operators `+`, `-`, `*`, `/`, `%`, `|`, `&`, `^`, `<<` and `>>` in component-wise fashion:
```cpp
float2x2 a {{0,0},{2,2}}; // a contains columns 0,0; 2,2
float2x2 b {{1,2},{1,2}}; // b contains columns 1,2; 1,2
float2x2 c = a + b; // c contains columns 1,2; 3,4
```
* supports binary operators with a scalar on the left or the right:
```cpp
auto m = 2 * float2x2{{1,2},{3,4}}; // m is float2x2{{2,4},{6,8}}
```
* supports operators `+=`, `-=`, `*=`, `/=`, `%=`, `|=`, `&=`, `^=`, `<<=` and `>>=` with matrices or scalars on the right:
```cpp
float2x2 v {{5,4},{3,2}};
v *= 3; // v is float2x2{{15,12},{9,6}}
```
* supports operations on mixed element types:
* supports [range-based for](https://en.cppreference.com/w/cpp/language/range-for) over columns
* has a flat memory layout
## Function listing
#### Vector algebra
* `cross(vec<T,3> a, vec<T,3> b) -> vec<T,3>` is the [cross or vector product](https://en.wikipedia.org/wiki/Cross_product) of vectors `a` and `b`
* `cross(vec<T,2> a, vec<T,2> b) -> T` is shorthand for `cross({a.x,a.y,0}, {b.x,b.y,0}).z`
* `cross(T a, vec<T,2> b) -> vec<T,2>` is shorthand for `cross({0,0,a.z}, {b.x,b.y,0}).xy()`
* `cross(vec<T,2> a, T b) -> vec<T,2>` is shorthand for `cross({a.x,a.y,0}, {0,0,b.z}).xy()`
* `dot(vec<T,M> a, vec<T,M> b) -> T` is the [dot or inner product](https://en.wikipedia.org/wiki/Dot_product) of vectors `a` and `b`
* `length(vec<T,M> a) -> T` is the length or magnitude of a vector `a`
* `length2(vec<T,M> a) -> T` is the *square* of the length or magnitude of vector `a`
* `normalize(vec<T,M> a) -> vec<T,M>` is a unit length vector in the same direction as `a` (undefined for zero-length vectors)
* `distance(vec<T,M> a, vec<T,M> b) -> T` is the [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance) between points `a` and `b`
* `distance2(vec<T,M> a, vec<T,M> b) -> T` is the *square* of the [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance) between points `a` and `b`
* `angle(vec<T,M> a, vec<T,M> b) -> T` is the angle in [radians](https://en.wikipedia.org/wiki/Radian) between vectors `a` and `b`
* `uangle(vec<T,M> a, vec<T,M> b) -> T` is the angle in [radians](https://en.wikipedia.org/wiki/Radian) between unit vectors `a` and `b` (undefined for non-unit vectors)
* `rot(T a, vec<T,2> v) -> vec<T,2>` is the vector `v` rotated counter-clockwise by the angle `a` in [radians](https://en.wikipedia.org/wiki/Radian)
* `nlerp(vec<T,M> a, vec<T,M> b, T t) -> vec<T,M>` is shorthand for `normalize(lerp(a,b,t))`
* `slerp(vec<T,M> a, vec<T,M> b, T t) -> vec<T,M>` is the [spherical linear interpolation](https://en.wikipedia.org/wiki/Slerp) between unit vectors `a` and `b` (undefined for non-unit vectors) by parameter `t`
#### Quaternion algebra
A small set of functions provides support for quaternion math, using `vec<T,4>` values to represent quaternions of the form `xi + yj + zk + w`.
* `qmul(vec<T,4> a, vec<T,4> b) -> vec<T,4>` is the [Hamilton product](https://en.wikipedia.org/wiki/Quaternion#Hamilton_product) of quaternions `a` and `b`
* `qconj(vec<T,4> q) -> vec<T,4>` is the [conjugate](https://en.wikipedia.org/wiki/Quaternion#Conjugation,_the_norm,_and_reciprocal) of quaternion `q`
* `qinv(vec<T,4> q) -> vec<T,4>` is the [inverse or reciprocal](https://en.wikipedia.org/wiki/Quaternion#Conjugation,_the_norm,_and_reciprocal) of quaternion `q` (undefined for zero-length quaternions)
* `qexp(vec<T,4> q) -> vec<T,4>` is the [exponential](https://en.wikipedia.org/wiki/Quaternion#Exponential,_logarithm,_and_power_functions) of quaternion `q`
* `qlog(vec<T,4> q) -> vec<T,4>` is the [logarithm](https://en.wikipedia.org/wiki/Quaternion#Exponential,_logarithm,_and_power_functions) of quaternion `q`
* `qpow(vec<T,4> q T p) -> vec<T,4>` is the quaternion `q` raised to the exponent `p`
A second set of functions provides support for using unit-length quaternions to represent 3D spatial rotations. Their results are undefined for quaternions which are not of unit-length.
* `qangle(vec<T,4> q)` is the angle in radians of the rotation expressed by quaternion `q`
* `qaxis(vec<T,4> q)` is the axis of rotation expression by quaternion `q` (undefined for zero-angle quaternions)
* `qrot(vec<T,4> q, vec<T,3> v) -> vec<T,3>` is vector `v` rotated via rotation quaternion `q`
* `qmat(vec<T,4> q)` is a 3x3 rotation matrix which performs the same operation as rotation quaternion `q`
* `qxdir(vec<T,4> q)` is (efficient) shorthand for `qrot(q, {1,0,0})`
* `qydir(vec<T,4> q)` is (efficient) shorthand for `qrot(q, {0,1,0})`
* `qzdir(vec<T,4> q)` is (efficient) shorthand for `qrot(q, {0,0,1})`
It is possible to use the `nlerp` and `slerp` functions to interpolate rotation quaternions as though they were simply four-dimensional vectors. However, the rotation quaternions form a [double cover](https://en.wikipedia.org/wiki/Covering_group) over spatial rotations in three dimensions. This means that there are **two** distinct rotation quaternions representing each spatial rotation. Naively interpolating between two spatial rotations using quaternions could follow the "short path" or the "long path" between these rotations, depending on which specific quaternions are being interpolated.
* `qnlerp(vec<T,4> a, vec<T,4> b, T t)` is similar to `nlerp(a,b,t)`, but always chooses the "short path" between the rotations represented by `a` and `b`.
* `qslerp(vec<T,4> a, vec<T,4> b, T t)` is similar to `slerp(a,b,t)`, but always chooses the "short path" between the rotations represented by `a` and `b`.
#### Matrix algebra
* `mul(mat<T,M,N> a, mat<T,N,P> b) -> mat<T,M,P>` is the [matrix product](https://en.wikipedia.org/wiki/Matrix_multiplication) of matrices `a` and `b`
** `mul(mat<T,M,N> a, vec<T,N> b) -> vec<T,M>` is the [matrix product](https://en.wikipedia.org/wiki/Matrix_multiplication) of matrix `a` and a column matrix containing the elements of vector `b`
** `mul(a, b, c)` is shorthand for `mul(mul(a, b), c)`
* `outerprod(vec<T,M> a, vec<T,N> b) -> mat<T,M,N>` is the [outer product](https://en.wikipedia.org/wiki/Outer_product) of vectors `a` and `b`
* `diagonal(mat<T,N,N> a) -> vec<T,N>` is a vector containing the elements along the main diagonal of matrix `a`
* `trace(mat<T,N,N> a) -> T` is the sum of the elements along the main diagonal of matrix `a`
* `transpose(mat<T,M,N> a) -> mat<T,N,M>` is the [transpose](https://en.wikipedia.org/wiki/Transpose) of matrix `a`
* `adjugate(mat<T,N,N> a) -> mat<T,N,N>` is the [adjugate or classical adjoint](https://en.wikipedia.org/wiki/Adjugate_matrix) of matrix `a` (the transpose of its cofactor matrix, or the numerator in the expression of its inverse)
* `comatrix(mat<T,N,N> a) -> mat<T,N,N>` is the [comatrix or cofactor matrix](https://en.wikipedia.org/wiki/Minor_(linear_algebra)#Inverse_of_a_matrix) of matrix `a` (the transpose of its adjugate matrix)
* `determinant(mat<T,N,N> a) -> T` is the [determinant](https://en.wikipedia.org/wiki/Determinant) of matrix `a`
* `inverse(mat<T,N,N> a) -> mat<T,N,N>` is the [multiplicative inverse](https://en.wikipedia.org/wiki/Multiplicative_inverse) of the [invertible matrix](https://en.wikipedia.org/wiki/Invertible_matrix) `a` (undefined for singular inputs)
#### Component-wise operations
The unary functions `abs`, `floor`, `ceil`, `exp`, `log`, `log10`, `sqrt`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `round` accept a vector-valued argument and produce a vector-valued result by passing individual elements to the function of the same name in the `std::` namespace, as defined by `<cmath>` or `<cstdlib>`.
```cpp
float4 a {1,-4,9,-16}; // a contains 1,-4,9,-16
float4 b = abs(a); // b contains 1,4,9,16
float4 c = sqrt(b); // c contains 1,2,3,4
```
The binary functions `fmod`, `pow`, `atan2`, and `copysign` function similarly, except that either argument can be a vector or a scalar.
```cpp
float2 a {5,4}, b {2,3};
float2 c = pow(a, 2); // c contains 25,16
float2 d = pow(2, b); // d contains 4,8
float2 e = pow(a, b); // e contains 25,64
```
The binary functions `equal`, `nequal`, `less`, `greater`, `lequal`, and `gequal` apply operators `==`, `!=`, `<`, `>`, `<=` and `>=` respectively in a component-wise fashion, returning a `vec<bool,M>`. As before, either argument can be a vector or a scalar.
```cpp
int2 a {2,5}, b {3,4};
bool2 c = less(a,3); // c contains true, false
bool2 d = equal(4,b); // d contains false, true
bool2 e = greater(a,b); // e contains false, true
```
`min(a,b) -> vec<T,M>` performs the component-wise selection of lesser elements, as by `a[i] < b[i] ? a[i] : b[i]`. Either argument can be a vector or a scalar.
`max(a,b) -> vec<T,M>` performs the component-wise selection of greater elements, as by `a[i] > b[i] ? a[i] : b[i]`. Either argument can be a vector or a scalar.
`clamp(x,l,h) -> vec<T,M>` performs the component-wise clamping of elements between a low and high boundary, as by `min(max(x,l),h)`. Any argument can be a vector or a scalar.
`select(p,a,b) -> vec<T,M>` performs a component-wise ternary operator, as by `p[i] ? a[i] : b[i]`. Any argument can be a vector or a scalar.
`lerp(a,b,t) -> vec<T,M>` performs a component-wise linear interpolation, as by `a[i]*(1-t[i]) + b[i]*t[i]`. Any argument can be a vector or a scalar.
#### Reductions
* `any(vec<bool,M> a) -> bool` is `true` if any element of the vector `a` is `true`
* `all(vec<bool,M> a) -> bool` is `true` if all elements of the vector `a` are `true`
* `sum(vec<T,M> a) -> T` is the sum of all elements in the vector `a`
* `product(vec<T,M> a) -> T` returns the product of all elements in the vector `a`
* `minelem(vec<T,M> a) -> T` returns the **value** of the least element in the vector `a`
* `maxelem(vec<T,M> a) -> T` returns the **value** of the greatest element in the vector `a`
* `argmin(vec<T,M> a) -> int` returns the **zero-based index** of the least element in the vector `a`
* `argmax(vec<T,M> a) -> int` returns the **zero-based index** of the greatest element in the vector `a`
#### Comparisons
`compare(a,b)` is conceptually equivalent to `operator <=>` from [C++20](https://en.cppreference.com/w/cpp/language/default_comparisons). It compares two values of equivalent shape and returns a value which supports all six standard comparisons against `0`. It provides the same ordering guarantees as the underlying scalar type. That is, a `vec<int,M>` provides a strong ordering, where a `vec<float,M>` provides a partial odering.
## Optional features
#### Type aliases
By default, `linalg.h` does not define any symbols in the global namespace, and a three-element vector of single-precision floating point values must be spelled `linalg::vec<float,3>`. In various libraries and shading languages, such a type might be spelled `float3`, `vec3`, `vec3f`, `point3f`, `simd_float3`, or any one of a hundred other possibilities. `linalg.h` provides a collection of useful aliases in the `linalg::aliases` namespace. If the names specified in this namespace are suitable for a user's purposes, they can quickly be brought into scope as follows:
```cpp
#include <linalg.h>
using namespace linalg::aliases;
float3 a_vector;
float4x4 a_matrix;
```
Note that this **only** brings the type aliases into global scope. The core types and all functions and operator overloads defined by the library remain in `namespace linalg`.
If the spellings in `namespace linalg::aliases` conflict with other types that have been defined in the global namespace or in other namespaces of interest, the user can choose to omit the `using namespace` directive and instead define their own aliases as desired.
```cpp
#include <linalg.h>
using v3f = linalg::vec<float,3>;
using m44f = linalg::mat<float,4,4>;
v3f a_vector;
m44f a_matrix;
```
It is, of course, always possible to use the core `linalg.h` types directly if operating in an environment where no additional symbols should be defined.
```cpp
#include <linalg.h>
linalg::vec<float,3> a_vector;
linalg::mat<float,4,4> a_matrix;
```
The set of type aliases defined in `namespace linalg::aliases` is as follows:
* `vec<float,M>` aliased to *floatM*, as in: `float1`, `float2`, `float3`, `float4`
* `vec<double,M>` aliased to *doubleM*, as in: `double1`, `double2`, `double3`, `double4`
* `vec<int,M>` aliased to *intM* as in: `int1`, `int2`, `int3`, `int4`
* `vec<unsigned,M>` aliased to *uintM* as in: `uint1`, `uint2`, `uint3`, `uint4`
* `vec<bool,M>` aliased to *boolM* as in: `bool1`, `bool2`, `bool3`, `bool4`
* `vec<int16_t,M>` aliased to *shortM* as in: `short1`, `short2`, `short3`, `short4`
* `vec<uint16_t,M>` aliased to *ushortM* as in: `ushort1`, `ushort2`, `ushort3`, `ushort4`
* `vec<uint8_t,M>` aliased to *byteM* as in: `byte1`, `byte2`, `byte3`, `byte4`
* `mat<float,M,N>` aliased to *floatMxN* as in: `float1x3`, `float3x2`, `float4x4`, etc.
* `mat<double,M,N>` aliased to *doubleMxN* as in: `double1x3`, `double3x2`, `double4x4`, etc.
* `mat<int,M,N>` aliased to *intMxN* as in: `int1x3`, `int3x2`, `int4x4`, etc.
* `mat<bool,M,N>` aliased to *boolMxN* as in: `boolx3`, `bool3x2`, `bool4x4`, etc.
All combinations of up to four elements, rows, or columns are provided.
#### `ostream` overloads
By default, `linalg.h` does not provide operators for interaction with standard library streams. This is to permit maximum flexibility for users who wish to define their own formatting (with or without delimiters, row versus column major matrices, human-readable precision or round-trip exact). However, as it is often useful to simply be able to show something when writing small programs, we provide some default stream operator overloads which can be brought into scope with:
```cpp
#include "linalg.h"
using namespace linalg::ostream_overloads;
```
The provided behavior is to output a string using the currently specified stream properties (width, precision, padding, etc) which matches the braced-initialization syntax that could be used to construct that same value, without any extra whitespace.
```cpp
int3 v {1, 2, 3};
int2x2 m {{4, 5}, {6, 7}};
std::cout << v << std::endl; // Prints {1,2,3}
std::wcout << m << std::endl; // Prints {{4,5},{6,7}}
```
#### User-defined conversions
A mechanism exists to define automatic conversions between `linalg` and user-provided types. As an example, this mechanism has already been used to defined bidirectional conversions between `linalg::vec<T,M>` and `std::array<T,M>`.
**TODO: Explain `converter<T,U>`**
## Higher order functions
#### `linalg::fold(f, a, b)`
`fold(f, a, b)` is a higher order function which accepts a function of the form `A,B => A` and repeatedly invokes `a = f(a, element_of_b)` until all elements have been consumed, before returning `a`. It is approximately equal to a [left fold with an initial value](https://en.wikipedia.org/wiki/Fold_(higher-order_function)). When `b` is a `vec<T,M>`, elements are folded from least to greatest index. When `b` is a `mat<T,M,N>`, elements are folded in column-major order.
See also: [Reductions](#reductions)
#### `linalg::apply(f, a...)`
`apply(f, a...)` is a higher order function which accepts a function of the form `A... => T` and applies it to component-wise sets of elements from data structures of compatible shape and dimensions. It is approximately equal to a [convolution](https://en.wikipedia.org/wiki/Convolution_(computer_science)) followed by a [map](https://en.wikipedia.org/wiki/Map_(higher-order_function)). The shape of the result (that is, whether it is a scalar, vector, or matrix, and the dimensions thereof) is determined by the arguments. If more than one argument is a non-scalar, the shape of those arguments must agree. Scalars can be freely intermixed with non-scalars, and element types can also be freely mixed. The element type of the returned value is determined by the return type of the provided mapping function `f`. The supported call signatures are enumerated in the following table:
| call | type of `a` | type of `b` | type of `c` | result type | result elements |
|------------------|--------------|--------------|-------------|--------------|--------------------------|
| `apply(f,a)` | `A` | | | `T` | `f(a)` |
| `apply(f,a)` | `vec<A,M>` | | | `vec<T,M>` | `f(a[i])...` |
| `apply(f,a)` | `mat<A,M,N>` | | | `mat<T,M,N>` | `f(a[j][i])...` |
| `apply(f,a,b)` | `A` | `B` | | `T` | `f(a, b)...` |
| `apply(f,a,b)` | `A` | `vec<B,M>` | | `vec<T,M>` | `f(a, b[i])...` |
| `apply(f,a,b)` | `vec<A,M>` | `B` | | `vec<T,M>` | `f(a[i], b)...` |
| `apply(f,a,b)` | `vec<A,M>` | `vec<B,M>` | | `vec<T,M>` | `f(a[i], b[i])...` |
| `apply(f,a,b)` | `A` | `mat<B,M,N>` | | `mat<T,M,N>` | `f(a, b[j][i])...` |
| `apply(f,a,b)` | `mat<A,M,N>` | `B` | | `mat<T,M,N>` | `f(a[j][i], b)...` |
| `apply(f,a,b)` | `mat<A,M,N>` | `mat<B,M,N>` | | `mat<T,M,N>` | `f(a[j][i], b[j][i])...` |
| `apply(f,a,b,c)` | `A` | `B` | `C` | `T` | `f(a, b, c)...` |
| `apply(f,a,b,c)` | `A` | `B` | `vec<C,M>` | `vec<T,M>` | `f(a, b, c[i])...` |
| `apply(f,a,b,c)` | `A` | `vec<B,M>` | `C` | `vec<T,M>` | `f(a, b[i], c)...` |
| `apply(f,a,b,c)` | `A` | `vec<B,M>` | `vec<C,M>` | `vec<T,M>` | `f(a, b[i], c[i])...` |
| `apply(f,a,b,c)` | `vec<A,M>` | `B` | `C` | `vec<T,M>` | `f(a[i], b, c)...` |
| `apply(f,a,b,c)` | `vec<A,M>` | `B` | `vec<C,M>` | `vec<T,M>` | `f(a[i], b, c[i])...` |
| `apply(f,a,b,c)` | `vec<A,M>` | `vec<B,M>` | `C` | `vec<T,M>` | `f(a[i], b[i], c)...` |
| `apply(f,a,b,c)` | `vec<A,M>` | `vec<B,M>` | `vec<C,M>` | `vec<T,M>` | `f(a[i], b[i], c[i])...` |
**TODO: Explain `apply_t<F, A...>` and SFINAE helpers.**
See also: [Component-wise operations](#component-wise-operations)
## Changes from `v2.1`
#### Improvements in `v2.2`
* `map(a,f)` and `zip(a,b,f)` subsumed by new `apply(f,a...)`
* `apply(...)` supports unary, binary, and ternary operations for `vec`
* `apply(...)` supports unary and binary operations for `mat` and `quat`
* `apply(...)` can also be invoked exclusively with scalars, and supports arbitrary numbers of arguments
* `apply(...)` supports mixed element types
* Template type alias `apply_t<F,A...>` provides the return type of `apply(f,a...)`
* `vec<T,1>` and `mat<T,M,1>` specializations are now provided
* `compare(a,b)` provide three-way comparison between compatible types
* `clamp(a,b,c)` can be invoked with three distinct (but compatible) types
* `select(a,b,c)` provides the a component-wise equivalent to `a ? b : c`
* `lerp(a,b,t)` has been generalized to a component-wise operation where any of `a`, `b`, and `t` can be vectors or scalars
* User can specialize `converter<T,U>` to enable implicit conversions from `U` to `T`, if either type is a `vec`, `mat`, or `quat`
* `identity` is implemented using this facility to serve as an in-library example
* No undefined behavior according to the C++11 standard
* Almost all operations which do not internally call `<cmath>` functions are `constexpr`, except for `argmin` and `argmax`
* No lambdas are used in `linalg.h`, avoiding potential ODR violations
#### Deprecations in `v2.2`
* `operator *` has been deprecated between pairs of matrices.
* Call `cmul(...)` if the original, component-wise product was intended
* Call `mul(...)` if the algebraic matrix product was intended
You can `#define LINALG_FORWARD_COMPATIBLE` before including `linalg.h` to remove all deprecated features.
#### Breaking changes in `v2.2-beta`
It is intended that compatibility will be restored before officially tagging `v2.2`
* `linalg.h` no longer supports Visual Studio 2013. However, it is known to work on GCC 4.9+, Clang 3.5+ in C++11 mode and Visual Studio 2015+.
* `vec<T,M>` and `mat<T,M,N>` may only be used with a `T` which is an [arithmetic type](https://en.cppreference.com/w/c/language/arithmetic_types)
* This requirement will likely be relaxed, but will require specializing some trait type to indicate additional scalar types

View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

View File

@ -0,0 +1,721 @@
// linalg.h - 2.2-beta - Single-header public domain linear algebra library
//
// The intent of this library is to provide the bulk of the functionality
// you need to write programs that frequently use small, fixed-size vectors
// and matrices, in domains such as computational geometry or computer
// graphics. It strives for terse, readable source code.
//
// The original author of this software is Sterling Orsten, and its permanent
// home is <http://github.com/sgorsten/linalg/>. If you find this software
// useful, an acknowledgement in your source text and/or product documentation
// is appreciated, but not required.
//
// The author acknowledges significant insights and contributions by:
// Stan Melax <http://github.com/melax/>
// Dimitri Diakopoulos <http://github.com/ddiakopoulos/>
//
// Some features are deprecated. Define LINALG_FORWARD_COMPATIBLE to remove them.
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org/>
#pragma once
#ifndef LINALG_H
#define LINALG_H
#include <cmath> // For various unary math functions, such as std::sqrt
#include <cstdlib> // To resolve std::abs ambiguity on clang
#include <cstdint> // For implementing namespace linalg::aliases
#include <array> // For std::array
#include <iosfwd> // For forward definitions of std::ostream
#include <type_traits> // For std::enable_if, std::is_same, std::declval
#include <functional> // For std::hash declaration
// In Visual Studio 2015, `constexpr` applied to a member function implies `const`, which causes ambiguous overload resolution
#if _MSC_VER <= 1900
#define LINALG_CONSTEXPR14
#else
#define LINALG_CONSTEXPR14 constexpr
#endif
namespace linalg
{
// Small, fixed-length vector type, consisting of exactly M elements of type T, and presumed to be a column-vector unless otherwise noted.
template<class T, int M> struct vec;
// Small, fixed-size matrix type, consisting of exactly M rows and N columns of type T, stored in column-major order.
template<class T, int M, int N> struct mat;
// Specialize converter<T,U> with a function application operator that converts type U to type T to enable implicit conversions
template<class T, class U> struct converter {};
namespace detail
{
template<class T, class U> using conv_t = typename std::enable_if<!std::is_same<T,U>::value, decltype(converter<T,U>{}(std::declval<U>()))>::type;
// Trait for retrieving scalar type of any linear algebra object
template<class A> struct scalar_type {};
template<class T, int M > struct scalar_type<vec<T,M >> { using type = T; };
template<class T, int M, int N> struct scalar_type<mat<T,M,N>> { using type = T; };
// Type returned by the compare(...) function which supports all six comparison operators against 0
template<class T> struct ord { T a,b; };
template<class T> constexpr bool operator == (const ord<T> & o, std::nullptr_t) { return o.a == o.b; }
template<class T> constexpr bool operator != (const ord<T> & o, std::nullptr_t) { return !(o.a == o.b); }
template<class T> constexpr bool operator < (const ord<T> & o, std::nullptr_t) { return o.a < o.b; }
template<class T> constexpr bool operator > (const ord<T> & o, std::nullptr_t) { return o.b < o.a; }
template<class T> constexpr bool operator <= (const ord<T> & o, std::nullptr_t) { return !(o.b < o.a); }
template<class T> constexpr bool operator >= (const ord<T> & o, std::nullptr_t) { return !(o.a < o.b); }
// Patterns which can be used with the compare(...) function
template<class A, class B> struct any_compare {};
template<class T> struct any_compare<vec<T,1>,vec<T,1>> { using type=ord<T>; constexpr ord<T> operator() (const vec<T,1> & a, const vec<T,1> & b) const { return ord<T>{a.x,b.x}; } };
template<class T> struct any_compare<vec<T,2>,vec<T,2>> { using type=ord<T>; constexpr ord<T> operator() (const vec<T,2> & a, const vec<T,2> & b) const { return !(a.x==b.x) ? ord<T>{a.x,b.x} : ord<T>{a.y,b.y}; } };
template<class T> struct any_compare<vec<T,3>,vec<T,3>> { using type=ord<T>; constexpr ord<T> operator() (const vec<T,3> & a, const vec<T,3> & b) const { return !(a.x==b.x) ? ord<T>{a.x,b.x} : !(a.y==b.y) ? ord<T>{a.y,b.y} : ord<T>{a.z,b.z}; } };
template<class T> struct any_compare<vec<T,4>,vec<T,4>> { using type=ord<T>; constexpr ord<T> operator() (const vec<T,4> & a, const vec<T,4> & b) const { return !(a.x==b.x) ? ord<T>{a.x,b.x} : !(a.y==b.y) ? ord<T>{a.y,b.y} : !(a.z==b.z) ? ord<T>{a.z,b.z} : ord<T>{a.w,b.w}; } };
template<class T, int M> struct any_compare<mat<T,M,1>,mat<T,M,1>> { using type=ord<T>; constexpr ord<T> operator() (const mat<T,M,1> & a, const mat<T,M,1> & b) const { return compare(a.x,b.x); } };
template<class T, int M> struct any_compare<mat<T,M,2>,mat<T,M,2>> { using type=ord<T>; constexpr ord<T> operator() (const mat<T,M,2> & a, const mat<T,M,2> & b) const { return a.x!=b.x ? compare(a.x,b.x) : compare(a.y,b.y); } };
template<class T, int M> struct any_compare<mat<T,M,3>,mat<T,M,3>> { using type=ord<T>; constexpr ord<T> operator() (const mat<T,M,3> & a, const mat<T,M,3> & b) const { return a.x!=b.x ? compare(a.x,b.x) : a.y!=b.y ? compare(a.y,b.y) : compare(a.z,b.z); } };
template<class T, int M> struct any_compare<mat<T,M,4>,mat<T,M,4>> { using type=ord<T>; constexpr ord<T> operator() (const mat<T,M,4> & a, const mat<T,M,4> & b) const { return a.x!=b.x ? compare(a.x,b.x) : a.y!=b.y ? compare(a.y,b.y) : a.z!=b.z ? compare(a.z,b.z) : compare(a.w,b.w); } };
// Helper for compile-time index-based access to members of vector and matrix types
template<int I> struct getter;
template<> struct getter<0> { template<class A> constexpr auto operator() (A & a) const -> decltype(a.x) { return a.x; } };
template<> struct getter<1> { template<class A> constexpr auto operator() (A & a) const -> decltype(a.y) { return a.y; } };
template<> struct getter<2> { template<class A> constexpr auto operator() (A & a) const -> decltype(a.z) { return a.z; } };
template<> struct getter<3> { template<class A> constexpr auto operator() (A & a) const -> decltype(a.w) { return a.w; } };
// Stand-in for std::integer_sequence/std::make_integer_sequence
template<int... I> struct seq {};
template<int A, int N> struct make_seq_impl;
template<int A> struct make_seq_impl<A,0> { using type=seq<>; };
template<int A> struct make_seq_impl<A,1> { using type=seq<A+0>; };
template<int A> struct make_seq_impl<A,2> { using type=seq<A+0,A+1>; };
template<int A> struct make_seq_impl<A,3> { using type=seq<A+0,A+1,A+2>; };
template<int A> struct make_seq_impl<A,4> { using type=seq<A+0,A+1,A+2,A+3>; };
template<int A, int B> using make_seq = typename make_seq_impl<A,B-A>::type;
template<class T, int M, int... I> vec<T,sizeof...(I)> constexpr swizzle(const vec<T,M> & v, seq<I...> i) { return {getter<I>{}(v)...}; }
template<class T, int M, int N, int... I, int... J> mat<T,sizeof...(I),sizeof...(J)> constexpr swizzle(const mat<T,M,N> & m, seq<I...> i, seq<J...> j) { return {swizzle(getter<J>{}(m),i)...}; }
// SFINAE helpers to determine result of function application
template<class F, class... T> using ret_t = decltype(std::declval<F>()(std::declval<T>()...));
// SFINAE helper which is defined if all provided types are scalars
struct empty {};
template<class... T> struct scalars;
template<> struct scalars<> { using type=void; };
template<class T, class... U> struct scalars<T,U...> : std::conditional<std::is_arithmetic<T>::value, scalars<U...>, empty>::type {};
template<class... T> using scalars_t = typename scalars<T...>::type;
// Helpers which indicate how apply(F, ...) should be called for various arguments
template<class F, class Void, class... T> struct apply {}; // Patterns which contain only vectors or scalars
template<class F, int M, class A > struct apply<F, scalars_t< >, vec<A,M> > { using type=vec<ret_t<F,A >,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, const vec<A,M> & a ) { return {f(getter<I>{}(a) )...}; } };
template<class F, int M, class A, class B > struct apply<F, scalars_t< >, vec<A,M>, vec<B,M> > { using type=vec<ret_t<F,A,B >,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, const vec<A,M> & a, const vec<B,M> & b ) { return {f(getter<I>{}(a), getter<I>{}(b) )...}; } };
template<class F, int M, class A, class B > struct apply<F, scalars_t<B >, vec<A,M>, B > { using type=vec<ret_t<F,A,B >,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, const vec<A,M> & a, B b ) { return {f(getter<I>{}(a), b )...}; } };
template<class F, int M, class A, class B > struct apply<F, scalars_t<A >, A, vec<B,M> > { using type=vec<ret_t<F,A,B >,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, A a, const vec<B,M> & b ) { return {f(a, getter<I>{}(b) )...}; } };
template<class F, int M, class A, class B, class C> struct apply<F, scalars_t< >, vec<A,M>, vec<B,M>, vec<C,M>> { using type=vec<ret_t<F,A,B,C>,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, const vec<A,M> & a, const vec<B,M> & b, const vec<C,M> & c) { return {f(getter<I>{}(a), getter<I>{}(b), getter<I>{}(c))...}; } };
template<class F, int M, class A, class B, class C> struct apply<F, scalars_t<C >, vec<A,M>, vec<B,M>, C > { using type=vec<ret_t<F,A,B,C>,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, const vec<A,M> & a, const vec<B,M> & b, C c) { return {f(getter<I>{}(a), getter<I>{}(b), c )...}; } };
template<class F, int M, class A, class B, class C> struct apply<F, scalars_t<B >, vec<A,M>, B, vec<C,M>> { using type=vec<ret_t<F,A,B,C>,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, const vec<A,M> & a, B b, const vec<C,M> & c) { return {f(getter<I>{}(a), b, getter<I>{}(c))...}; } };
template<class F, int M, class A, class B, class C> struct apply<F, scalars_t<B,C>, vec<A,M>, B, C > { using type=vec<ret_t<F,A,B,C>,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, const vec<A,M> & a, B b, C c) { return {f(getter<I>{}(a), b, c )...}; } };
template<class F, int M, class A, class B, class C> struct apply<F, scalars_t<A >, A, vec<B,M>, vec<C,M>> { using type=vec<ret_t<F,A,B,C>,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, A a, const vec<B,M> & b, const vec<C,M> & c) { return {f(a, getter<I>{}(b), getter<I>{}(c))...}; } };
template<class F, int M, class A, class B, class C> struct apply<F, scalars_t<A,C>, A, vec<B,M>, C > { using type=vec<ret_t<F,A,B,C>,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, A a, const vec<B,M> & b, C c) { return {f(a, getter<I>{}(b), c )...}; } };
template<class F, int M, class A, class B, class C> struct apply<F, scalars_t<A,B>, A, B, vec<C,M>> { using type=vec<ret_t<F,A,B,C>,M>; enum {size=M, mm=0}; template<int... I> static constexpr type impl(seq<I...>, F f, A a, B b, const vec<C,M> & c) { return {f(a, b, getter<I>{}(c))...}; } };
template<class F, int M, int N, class A > struct apply<F, scalars_t< >, mat<A,M,N> > { using type=mat<ret_t<F,A >,M,N>; enum {size=N, mm=0}; template<int... J> static constexpr type impl(seq<J...>, F f, const mat<A,M,N> & a ) { return {apply<F, void, vec<A,M> >::impl(make_seq<0,M>{}, f, getter<J>{}(a) )...}; } };
template<class F, int M, int N, class A, class B> struct apply<F, scalars_t< >, mat<A,M,N>, mat<B,M,N>> { using type=mat<ret_t<F,A,B>,M,N>; enum {size=N, mm=1}; template<int... J> static constexpr type impl(seq<J...>, F f, const mat<A,M,N> & a, const mat<B,M,N> & b) { return {apply<F, void, vec<A,M>, vec<B,M>>::impl(make_seq<0,M>{}, f, getter<J>{}(a), getter<J>{}(b))...}; } };
template<class F, int M, int N, class A, class B> struct apply<F, scalars_t<B>, mat<A,M,N>, B > { using type=mat<ret_t<F,A,B>,M,N>; enum {size=N, mm=0}; template<int... J> static constexpr type impl(seq<J...>, F f, const mat<A,M,N> & a, B b) { return {apply<F, void, vec<A,M>, B >::impl(make_seq<0,M>{}, f, getter<J>{}(a), b )...}; } };
template<class F, int M, int N, class A, class B> struct apply<F, scalars_t<A>, A, mat<B,M,N>> { using type=mat<ret_t<F,A,B>,M,N>; enum {size=N, mm=0}; template<int... J> static constexpr type impl(seq<J...>, F f, A a, const mat<B,M,N> & b) { return {apply<F, void, A, vec<B,M>>::impl(make_seq<0,M>{}, f, a, getter<J>{}(b))...}; } };
template<class F, class... A> struct apply<F, scalars_t<A...>, A...> { using type = ret_t<F,A...>; enum {size=0, mm=0}; static constexpr type impl(seq<>, F f, A... a) { return f(a...); } };
// Function objects for selecting between alternatives
struct min { template<class A, class B> constexpr auto operator() (A a, B b) const -> typename std::remove_reference<decltype(a<b ? a : b)>::type { return a<b ? a : b; } };
struct max { template<class A, class B> constexpr auto operator() (A a, B b) const -> typename std::remove_reference<decltype(a<b ? b : a)>::type { return a<b ? b : a; } };
struct clamp { template<class A, class B, class C> constexpr auto operator() (A a, B b, C c) const -> typename std::remove_reference<decltype(a<b ? b : a<c ? a : c)>::type { return a<b ? b : a<c ? a : c; } };
struct select { template<class A, class B, class C> constexpr auto operator() (A a, B b, C c) const -> typename std::remove_reference<decltype(a ? b : c)>::type { return a ? b : c; } };
struct lerp { template<class A, class B, class C> constexpr auto operator() (A a, B b, C c) const -> decltype(a*(1-c) + b*c) { return a*(1-c) + b*c; } };
// Function objects for applying operators
struct op_pos { template<class A> constexpr auto operator() (A a) const -> decltype(+a) { return +a; } };
struct op_neg { template<class A> constexpr auto operator() (A a) const -> decltype(-a) { return -a; } };
struct op_not { template<class A> constexpr auto operator() (A a) const -> decltype(!a) { return !a; } };
struct op_cmp { template<class A> constexpr auto operator() (A a) const -> decltype(~(a)) { return ~a; } };
struct op_mul { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a * b) { return a * b; } };
struct op_div { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a / b) { return a / b; } };
struct op_mod { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a % b) { return a % b; } };
struct op_add { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a + b) { return a + b; } };
struct op_sub { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a - b) { return a - b; } };
struct op_lsh { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a << b) { return a << b; } };
struct op_rsh { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a >> b) { return a >> b; } };
struct op_lt { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a < b) { return a < b; } };
struct op_gt { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a > b) { return a > b; } };
struct op_le { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a <= b) { return a <= b; } };
struct op_ge { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a >= b) { return a >= b; } };
struct op_eq { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a == b) { return a == b; } };
struct op_ne { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a != b) { return a != b; } };
struct op_int { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a & b) { return a & b; } };
struct op_xor { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a ^ b) { return a ^ b; } };
struct op_un { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a | b) { return a | b; } };
struct op_and { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a && b) { return a && b; } };
struct op_or { template<class A, class B> constexpr auto operator() (A a, B b) const -> decltype(a || b) { return a || b; } };
// Function objects for applying standard library math functions
struct std_abs { template<class A> auto operator() (A a) const -> decltype(std::abs (a)) { return std::abs (a); } };
struct std_floor { template<class A> auto operator() (A a) const -> decltype(std::floor(a)) { return std::floor(a); } };
struct std_ceil { template<class A> auto operator() (A a) const -> decltype(std::ceil (a)) { return std::ceil (a); } };
struct std_exp { template<class A> auto operator() (A a) const -> decltype(std::exp (a)) { return std::exp (a); } };
struct std_log { template<class A> auto operator() (A a) const -> decltype(std::log (a)) { return std::log (a); } };
struct std_log10 { template<class A> auto operator() (A a) const -> decltype(std::log10(a)) { return std::log10(a); } };
struct std_sqrt { template<class A> auto operator() (A a) const -> decltype(std::sqrt (a)) { return std::sqrt (a); } };
struct std_sin { template<class A> auto operator() (A a) const -> decltype(std::sin (a)) { return std::sin (a); } };
struct std_cos { template<class A> auto operator() (A a) const -> decltype(std::cos (a)) { return std::cos (a); } };
struct std_tan { template<class A> auto operator() (A a) const -> decltype(std::tan (a)) { return std::tan (a); } };
struct std_asin { template<class A> auto operator() (A a) const -> decltype(std::asin (a)) { return std::asin (a); } };
struct std_acos { template<class A> auto operator() (A a) const -> decltype(std::acos (a)) { return std::acos (a); } };
struct std_atan { template<class A> auto operator() (A a) const -> decltype(std::atan (a)) { return std::atan (a); } };
struct std_sinh { template<class A> auto operator() (A a) const -> decltype(std::sinh (a)) { return std::sinh (a); } };
struct std_cosh { template<class A> auto operator() (A a) const -> decltype(std::cosh (a)) { return std::cosh (a); } };
struct std_tanh { template<class A> auto operator() (A a) const -> decltype(std::tanh (a)) { return std::tanh (a); } };
struct std_round { template<class A> auto operator() (A a) const -> decltype(std::round(a)) { return std::round(a); } };
struct std_fmod { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::fmod (a, b)) { return std::fmod (a, b); } };
struct std_pow { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::pow (a, b)) { return std::pow (a, b); } };
struct std_atan2 { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::atan2 (a, b)) { return std::atan2 (a, b); } };
struct std_copysign { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::copysign(a, b)) { return std::copysign(a, b); } };
}
// Small, fixed-length vector type, consisting of exactly M elements of type T, and presumed to be a column-vector unless otherwise noted
template<class T> struct vec<T,1>
{
T x;
constexpr vec() : x() {}
constexpr vec(const T & x_) : x(x_) {}
// NOTE: vec<T,1> does NOT have a constructor from pointer, this can conflict with initializing its single element from zero
template<class U>
constexpr explicit vec(const vec<U,1> & v) : vec(static_cast<T>(v.x)) {}
constexpr const T & operator[] (int i) const { return x; }
LINALG_CONSTEXPR14 T & operator[] (int i) { return x; }
template<class U, class=detail::conv_t<vec,U>> constexpr vec(const U & u) : vec(converter<vec,U>{}(u)) {}
template<class U, class=detail::conv_t<U,vec>> constexpr operator U () const { return converter<U,vec>{}(*this); }
};
template<class T> struct vec<T,2>
{
T x,y;
constexpr vec() : x(), y() {}
constexpr vec(const T & x_, const T & y_) : x(x_), y(y_) {}
constexpr explicit vec(const T & s) : vec(s, s) {}
constexpr explicit vec(const T * p) : vec(p[0], p[1]) {}
template<class U>
constexpr explicit vec(const vec<U,2> & v) : vec(static_cast<T>(v.x), static_cast<T>(v.y)) {}
constexpr const T & operator[] (int i) const { return i==0?x:y; }
LINALG_CONSTEXPR14 T & operator[] (int i) { return i==0?x:y; }
template<class U, class=detail::conv_t<vec,U>> constexpr vec(const U & u) : vec(converter<vec,U>{}(u)) {}
template<class U, class=detail::conv_t<U,vec>> constexpr operator U () const { return converter<U,vec>{}(*this); }
};
template<class T> struct vec<T,3>
{
T x,y,z;
constexpr vec() : x(), y(), z() {}
constexpr vec(const T & x_, const T & y_,
const T & z_) : x(x_), y(y_), z(z_) {}
constexpr vec(const vec<T,2> & xy,
const T & z_) : vec(xy.x, xy.y, z_) {}
constexpr explicit vec(const T & s) : vec(s, s, s) {}
constexpr explicit vec(const T * p) : vec(p[0], p[1], p[2]) {}
template<class U>
constexpr explicit vec(const vec<U,3> & v) : vec(static_cast<T>(v.x), static_cast<T>(v.y), static_cast<T>(v.z)) {}
constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:z; }
LINALG_CONSTEXPR14 T & operator[] (int i) { return i==0?x:i==1?y:z; }
constexpr const vec<T,2> & xy() const { return *reinterpret_cast<const vec<T,2> *>(this); }
vec<T,2> & xy() { return *reinterpret_cast<vec<T,2> *>(this); }
template<class U, class=detail::conv_t<vec,U>> constexpr vec(const U & u) : vec(converter<vec,U>{}(u)) {}
template<class U, class=detail::conv_t<U,vec>> constexpr operator U () const { return converter<U,vec>{}(*this); }
};
template<class T> struct vec<T,4>
{
T x,y,z,w;
constexpr vec() : x(), y(), z(), w() {}
constexpr vec(const T & x_, const T & y_,
const T & z_, const T & w_) : x(x_), y(y_), z(z_), w(w_) {}
constexpr vec(const vec<T,2> & xy,
const T & z_, const T & w_) : vec(xy.x, xy.y, z_, w_) {}
constexpr vec(const vec<T,3> & xyz,
const T & w_) : vec(xyz.x, xyz.y, xyz.z, w_) {}
constexpr explicit vec(const T & s) : vec(s, s, s, s) {}
constexpr explicit vec(const T * p) : vec(p[0], p[1], p[2], p[3]) {}
template<class U>
constexpr explicit vec(const vec<U,4> & v) : vec(static_cast<T>(v.x), static_cast<T>(v.y), static_cast<T>(v.z), static_cast<T>(v.w)) {}
constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:i==2?z:w; }
LINALG_CONSTEXPR14 T & operator[] (int i) { return i==0?x:i==1?y:i==2?z:w; }
constexpr const vec<T,2> & xy() const { return *reinterpret_cast<const vec<T,2> *>(this); }
constexpr const vec<T,3> & xyz() const { return *reinterpret_cast<const vec<T,3> *>(this); }
vec<T,2> & xy() { return *reinterpret_cast<vec<T,2> *>(this); }
vec<T,3> & xyz() { return *reinterpret_cast<vec<T,3> *>(this); }
template<class U, class=detail::conv_t<vec,U>> constexpr vec(const U & u) : vec(converter<vec,U>{}(u)) {}
template<class U, class=detail::conv_t<U,vec>> constexpr operator U () const { return converter<U,vec>{}(*this); }
};
// Small, fixed-size matrix type, consisting of exactly M rows and N columns of type T, stored in column-major order.
template<class T, int M> struct mat<T,M,1>
{
typedef vec<T,M> V;
V x;
constexpr mat() : x() {}
constexpr mat(const V & x_) : x(x_) {}
constexpr explicit mat(const T & s) : x(s) {}
constexpr explicit mat(const T * p) : x(p+M*0) {}
template<class U>
constexpr explicit mat(const mat<U,M,1> & m) : mat(V(m.x)) {}
constexpr vec<T,1> row(int i) const { return {x[i]}; }
constexpr const V & operator[] (int j) const { return x; }
LINALG_CONSTEXPR14 V & operator[] (int j) { return x; }
template<class U, class=detail::conv_t<mat,U>> constexpr mat(const U & u) : mat(converter<mat,U>{}(u)) {}
template<class U, class=detail::conv_t<U,mat>> constexpr operator U () const { return converter<U,mat>{}(*this); }
};
template<class T, int M> struct mat<T,M,2>
{
typedef vec<T,M> V;
V x,y;
constexpr mat() : x(), y() {}
constexpr mat(const V & x_, const V & y_) : x(x_), y(y_) {}
constexpr explicit mat(const T & s) : x(s), y(s) {}
constexpr explicit mat(const T * p) : x(p+M*0), y(p+M*1) {}
template<class U>
constexpr explicit mat(const mat<U,M,2> & m) : mat(V(m.x), V(m.y)) {}
constexpr vec<T,2> row(int i) const { return {x[i], y[i]}; }
constexpr const V & operator[] (int j) const { return j==0?x:y; }
LINALG_CONSTEXPR14 V & operator[] (int j) { return j==0?x:y; }
template<class U, class=detail::conv_t<mat,U>> constexpr mat(const U & u) : mat(converter<mat,U>{}(u)) {}
template<class U, class=detail::conv_t<U,mat>> constexpr operator U () const { return converter<U,mat>{}(*this); }
};
template<class T, int M> struct mat<T,M,3>
{
typedef vec<T,M> V;
V x,y,z;
constexpr mat() : x(), y(), z() {}
constexpr mat(const V & x_, const V & y_,
const V & z_) : x(x_), y(y_), z(z_) {}
constexpr explicit mat(const T & s) : x(s), y(s), z(s) {}
constexpr explicit mat(const T * p) : x(p+M*0), y(p+M*1), z(p+M*2) {}
template<class U>
constexpr explicit mat(const mat<U,M,3> & m) : mat(V(m.x), V(m.y), V(m.z)) {}
constexpr vec<T,3> row(int i) const { return {x[i], y[i], z[i]}; }
constexpr const V & operator[] (int j) const { return j==0?x:j==1?y:z; }
LINALG_CONSTEXPR14 V & operator[] (int j) { return j==0?x:j==1?y:z; }
template<class U, class=detail::conv_t<mat,U>> constexpr mat(const U & u) : mat(converter<mat,U>{}(u)) {}
template<class U, class=detail::conv_t<U,mat>> constexpr operator U () const { return converter<U,mat>{}(*this); }
};
template<class T, int M> struct mat<T,M,4>
{
typedef vec<T,M> V;
V x,y,z,w;
constexpr mat() : x(), y(), z(), w() {}
constexpr mat(const V & x_, const V & y_,
const V & z_, const V & w_) : x(x_), y(y_), z(z_), w(w_) {}
constexpr explicit mat(const T & s) : x(s), y(s), z(s), w(s) {}
constexpr explicit mat(const T * p) : x(p+M*0), y(p+M*1), z(p+M*2), w(p+M*3) {}
template<class U>
constexpr explicit mat(const mat<U,M,4> & m) : mat(V(m.x), V(m.y), V(m.z), V(m.w)) {}
constexpr vec<T,4> row(int i) const { return {x[i], y[i], z[i], w[i]}; }
constexpr const V & operator[] (int j) const { return j==0?x:j==1?y:j==2?z:w; }
LINALG_CONSTEXPR14 V & operator[] (int j) { return j==0?x:j==1?y:j==2?z:w; }
template<class U, class=detail::conv_t<mat,U>> constexpr mat(const U & u) : mat(converter<mat,U>{}(u)) {}
template<class U, class=detail::conv_t<U,mat>> constexpr operator U () const { return converter<U,mat>{}(*this); }
};
// Define a type which will convert to the multiplicative identity of any square matrix
struct identity_t { constexpr explicit identity_t(int) {} };
template<class T> struct converter<mat<T,1,1>, identity_t> { constexpr mat<T,1,1> operator() (identity_t) const { return {vec<T,1>{1}}; } };
template<class T> struct converter<mat<T,2,2>, identity_t> { constexpr mat<T,2,2> operator() (identity_t) const { return {{1,0},{0,1}}; } };
template<class T> struct converter<mat<T,3,3>, identity_t> { constexpr mat<T,3,3> operator() (identity_t) const { return {{1,0,0},{0,1,0},{0,0,1}}; } };
template<class T> struct converter<mat<T,4,4>, identity_t> { constexpr mat<T,4,4> operator() (identity_t) const { return {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}; } };
constexpr identity_t identity {1};
// Produce a scalar by applying f(A,B) -> A to adjacent pairs of elements from a vec/mat in left-to-right/column-major order (matching the associativity of arithmetic and logical operators)
template<class F, class A, class B> constexpr A fold(F f, A a, const vec<B,1> & b) { return f(a, b.x); }
template<class F, class A, class B> constexpr A fold(F f, A a, const vec<B,2> & b) { return f(f(a, b.x), b.y); }
template<class F, class A, class B> constexpr A fold(F f, A a, const vec<B,3> & b) { return f(f(f(a, b.x), b.y), b.z); }
template<class F, class A, class B> constexpr A fold(F f, A a, const vec<B,4> & b) { return f(f(f(f(a, b.x), b.y), b.z), b.w); }
template<class F, class A, class B, int M> constexpr A fold(F f, A a, const mat<B,M,1> & b) { return fold(f, a, b.x); }
template<class F, class A, class B, int M> constexpr A fold(F f, A a, const mat<B,M,2> & b) { return fold(f, fold(f, a, b.x), b.y); }
template<class F, class A, class B, int M> constexpr A fold(F f, A a, const mat<B,M,3> & b) { return fold(f, fold(f, fold(f, a, b.x), b.y), b.z); }
template<class F, class A, class B, int M> constexpr A fold(F f, A a, const mat<B,M,4> & b) { return fold(f, fold(f, fold(f, fold(f, a, b.x), b.y), b.z), b.w); }
// Type aliases for the result of calling apply(...) with various arguments, can be used with return type SFINAE to constrian overload sets
template<class F, class... A> using apply_t = typename detail::apply<F,void,A...>::type;
template<class F, class... A> using mm_apply_t = typename std::enable_if<detail::apply<F,void,A...>::mm, apply_t<F,A...>>::type;
template<class F, class... A> using no_mm_apply_t = typename std::enable_if<!detail::apply<F,void,A...>::mm, apply_t<F,A...>>::type;
template<class A> using scalar_t = typename detail::scalar_type<A>::type; // Underlying scalar type when performing elementwise operations
// apply(f,...) applies the provided function in an elementwise fashion to its arguments, producing an object of the same dimensions
template<class F, class... A> constexpr apply_t<F,A...> apply(F func, const A & ... args) { return detail::apply<F,void,A...>::impl(detail::make_seq<0,detail::apply<F,void,A...>::size>{}, func, args...); }
// map(a,f) is equivalent to apply(f,a)
template<class A, class F> constexpr apply_t<F,A> map(const A & a, F func) { return apply(func, a); }
// zip(a,b,f) is equivalent to apply(f,a,b)
template<class A, class B, class F> constexpr apply_t<F,A,B> zip(const A & a, const B & b, F func) { return apply(func, a, b); }
// Relational operators are defined to compare the elements of two vectors or matrices lexicographically, in column-major order
template<class A, class B> constexpr typename detail::any_compare<A,B>::type compare(const A & a, const B & b) { return detail::any_compare<A,B>()(a,b); }
template<class A, class B> constexpr auto operator == (const A & a, const B & b) -> decltype(compare(a,b) == 0) { return compare(a,b) == 0; }
template<class A, class B> constexpr auto operator != (const A & a, const B & b) -> decltype(compare(a,b) != 0) { return compare(a,b) != 0; }
template<class A, class B> constexpr auto operator < (const A & a, const B & b) -> decltype(compare(a,b) < 0) { return compare(a,b) < 0; }
template<class A, class B> constexpr auto operator > (const A & a, const B & b) -> decltype(compare(a,b) > 0) { return compare(a,b) > 0; }
template<class A, class B> constexpr auto operator <= (const A & a, const B & b) -> decltype(compare(a,b) <= 0) { return compare(a,b) <= 0; }
template<class A, class B> constexpr auto operator >= (const A & a, const B & b) -> decltype(compare(a,b) >= 0) { return compare(a,b) >= 0; }
// Functions for coalescing scalar values
template<class A> constexpr bool any (const A & a) { return fold(detail::op_or{}, false, a); }
template<class A> constexpr bool all (const A & a) { return fold(detail::op_and{}, true, a); }
template<class A> constexpr scalar_t<A> sum (const A & a) { return fold(detail::op_add{}, scalar_t<A>(0), a); }
template<class A> constexpr scalar_t<A> product(const A & a) { return fold(detail::op_mul{}, scalar_t<A>(1), a); }
template<class A> constexpr scalar_t<A> minelem(const A & a) { return fold(detail::min{}, a.x, a); }
template<class A> constexpr scalar_t<A> maxelem(const A & a) { return fold(detail::max{}, a.x, a); }
template<class T, int M> int argmin(const vec<T,M> & a) { int j=0; for(int i=1; i<M; ++i) if(a[i] < a[j]) j = i; return j; }
template<class T, int M> int argmax(const vec<T,M> & a) { int j=0; for(int i=1; i<M; ++i) if(a[i] > a[j]) j = i; return j; }
// Unary operators are defined component-wise for linalg types
template<class A> constexpr apply_t<detail::op_pos, A> operator + (const A & a) { return apply(detail::op_pos{}, a); }
template<class A> constexpr apply_t<detail::op_neg, A> operator - (const A & a) { return apply(detail::op_neg{}, a); }
template<class A> constexpr apply_t<detail::op_cmp, A> operator ~ (const A & a) { return apply(detail::op_cmp{}, a); }
template<class A> constexpr apply_t<detail::op_not, A> operator ! (const A & a) { return apply(detail::op_not{}, a); }
// Binary operators are defined component-wise for linalg types, EXCEPT for `operator *`
template<class A, class B> constexpr apply_t<detail::op_add, A, B> operator + (const A & a, const B & b) { return apply(detail::op_add{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_sub, A, B> operator - (const A & a, const B & b) { return apply(detail::op_sub{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_mul, A, B> cmul (const A & a, const B & b) { return apply(detail::op_mul{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_div, A, B> operator / (const A & a, const B & b) { return apply(detail::op_div{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_mod, A, B> operator % (const A & a, const B & b) { return apply(detail::op_mod{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_un, A, B> operator | (const A & a, const B & b) { return apply(detail::op_un{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_xor, A, B> operator ^ (const A & a, const B & b) { return apply(detail::op_xor{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_int, A, B> operator & (const A & a, const B & b) { return apply(detail::op_int{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_lsh, A, B> operator << (const A & a, const B & b) { return apply(detail::op_lsh{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_rsh, A, B> operator >> (const A & a, const B & b) { return apply(detail::op_rsh{}, a, b); }
// Binary `operator *` was originally defined component-wise for all patterns, in a fashion consistent with the other operators. However,
// this was one of the most frequent sources of confusion among new users of this library, with the binary `operator *` being used accidentally
// by users who INTENDED the semantics of the algebraic matrix product, but RECEIVED the semantics of the Hadamard product. While there is
// precedent within the HLSL, Fortran, R, APL, J, and Mathematica programming languages for `operator *` having the semantics of the Hadamard
// product between matrices, it is counterintuitive to users of GLSL, Eigen, and many other languages and libraries that chose matrix product
// semantics for `operator *`.
//
// For these reasons, binary `operator *` is now DEPRECATED between pairs of matrices. Users may call `cmul(...)` for component-wise multiplication,
// or `mul(...)` for matrix multiplication. Binary `operator *` continues to be available for vector * vector, vector * scalar, matrix * scalar, etc.
template<class A, class B> constexpr no_mm_apply_t<detail::op_mul, A, B> operator * (const A & a, const B & b) { return cmul(a,b); }
#ifndef LINALG_FORWARD_COMPATIBLE
template<class A, class B> [[deprecated("`operator *` between pairs of matrices is deprecated. See the source text for details.")]] constexpr mm_apply_t<detail::op_mul, A, B> operator * (const A & a, const B & b) { return cmul(a,b); }
#endif
// Binary assignment operators a $= b is always defined as though it were explicitly written a = a $ b
template<class A, class B> constexpr auto operator += (A & a, const B & b) -> decltype(a = a + b) { return a = a + b; }
template<class A, class B> constexpr auto operator -= (A & a, const B & b) -> decltype(a = a - b) { return a = a - b; }
template<class A, class B> constexpr auto operator *= (A & a, const B & b) -> decltype(a = a * b) { return a = a * b; }
template<class A, class B> constexpr auto operator /= (A & a, const B & b) -> decltype(a = a / b) { return a = a / b; }
template<class A, class B> constexpr auto operator %= (A & a, const B & b) -> decltype(a = a % b) { return a = a % b; }
template<class A, class B> constexpr auto operator |= (A & a, const B & b) -> decltype(a = a | b) { return a = a | b; }
template<class A, class B> constexpr auto operator ^= (A & a, const B & b) -> decltype(a = a ^ b) { return a = a ^ b; }
template<class A, class B> constexpr auto operator &= (A & a, const B & b) -> decltype(a = a & b) { return a = a & b; }
template<class A, class B> constexpr auto operator <<= (A & a, const B & b) -> decltype(a = a << b) { return a = a << b; }
template<class A, class B> constexpr auto operator >>= (A & a, const B & b) -> decltype(a = a >> b) { return a = a >> b; }
// Swizzles and subobjects
template<int... I, class T, int M> constexpr vec<T,sizeof...(I)> swizzle(const vec<T,M> & a) { return {detail::getter<I>{}(a)...}; }
template<int I0, int I1, class T, int M> constexpr vec<T,I1-I0> subvec (const vec<T,M> & a) { return detail::swizzle(a, detail::make_seq<I0,I1>{}); }
template<int I0, int J0, int I1, int J1, class T, int M, int N> constexpr mat<T,I1-I0,J1-J0> submat (const mat<T,M,N> & a) { return detail::swizzle(a, detail::make_seq<I0,I1>{}, detail::make_seq<J0,J1>{}); }
// Component-wise standard library math functions
template<class A> apply_t<detail::std_abs, A> abs (const A & a) { return apply(detail::std_abs{}, a); }
template<class A> apply_t<detail::std_floor, A> floor(const A & a) { return apply(detail::std_floor{}, a); }
template<class A> apply_t<detail::std_ceil, A> ceil (const A & a) { return apply(detail::std_ceil{}, a); }
template<class A> apply_t<detail::std_exp, A> exp (const A & a) { return apply(detail::std_exp{}, a); }
template<class A> apply_t<detail::std_log, A> log (const A & a) { return apply(detail::std_log{}, a); }
template<class A> apply_t<detail::std_log10, A> log10(const A & a) { return apply(detail::std_log10{}, a); }
template<class A> apply_t<detail::std_sqrt, A> sqrt (const A & a) { return apply(detail::std_sqrt{}, a); }
template<class A> apply_t<detail::std_sin, A> sin (const A & a) { return apply(detail::std_sin{}, a); }
template<class A> apply_t<detail::std_cos, A> cos (const A & a) { return apply(detail::std_cos{}, a); }
template<class A> apply_t<detail::std_tan, A> tan (const A & a) { return apply(detail::std_tan{}, a); }
template<class A> apply_t<detail::std_asin, A> asin (const A & a) { return apply(detail::std_asin{}, a); }
template<class A> apply_t<detail::std_acos, A> acos (const A & a) { return apply(detail::std_acos{}, a); }
template<class A> apply_t<detail::std_atan, A> atan (const A & a) { return apply(detail::std_atan{}, a); }
template<class A> apply_t<detail::std_sinh, A> sinh (const A & a) { return apply(detail::std_sinh{}, a); }
template<class A> apply_t<detail::std_cosh, A> cosh (const A & a) { return apply(detail::std_cosh{}, a); }
template<class A> apply_t<detail::std_tanh, A> tanh (const A & a) { return apply(detail::std_tanh{}, a); }
template<class A> apply_t<detail::std_round, A> round(const A & a) { return apply(detail::std_round{}, a); }
template<class A, class B> apply_t<detail::std_fmod, A, B> fmod (const A & a, const B & b) { return apply(detail::std_fmod{}, a, b); }
template<class A, class B> apply_t<detail::std_pow, A, B> pow (const A & a, const B & b) { return apply(detail::std_pow{}, a, b); }
template<class A, class B> apply_t<detail::std_atan2, A, B> atan2 (const A & a, const B & b) { return apply(detail::std_atan2{}, a, b); }
template<class A, class B> apply_t<detail::std_copysign, A, B> copysign(const A & a, const B & b) { return apply(detail::std_copysign{}, a, b); }
// Component-wise relational functions on vectors
template<class A, class B> constexpr apply_t<detail::op_eq, A, B> equal (const A & a, const B & b) { return apply(detail::op_eq{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_ne, A, B> nequal (const A & a, const B & b) { return apply(detail::op_ne{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_lt, A, B> less (const A & a, const B & b) { return apply(detail::op_lt{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_gt, A, B> greater(const A & a, const B & b) { return apply(detail::op_gt{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_le, A, B> lequal (const A & a, const B & b) { return apply(detail::op_le{}, a, b); }
template<class A, class B> constexpr apply_t<detail::op_ge, A, B> gequal (const A & a, const B & b) { return apply(detail::op_ge{}, a, b); }
// Component-wise selection functions on vectors
template<class A, class B> constexpr apply_t<detail::min, A, B> min(const A & a, const B & b) { return apply(detail::min{}, a, b); }
template<class A, class B> constexpr apply_t<detail::max, A, B> max(const A & a, const B & b) { return apply(detail::max{}, a, b); }
template<class X, class L, class H> constexpr apply_t<detail::clamp, X, L, H> clamp (const X & x, const L & l, const H & h) { return apply(detail::clamp{}, x, l, h); }
template<class P, class A, class B> constexpr apply_t<detail::select, P, A, B> select(const P & p, const A & a, const B & b) { return apply(detail::select{}, p, a, b); }
template<class A, class B, class T> constexpr apply_t<detail::lerp, A, B, T> lerp (const A & a, const B & b, const T & t) { return apply(detail::lerp{}, a, b, t); }
// Support for vector algebra
template<class T> constexpr T cross (const vec<T,2> & a, const vec<T,2> & b) { return a.x*b.y-a.y*b.x; }
template<class T> constexpr vec<T,2> cross (T a, const vec<T,2> & b) { return {-a*b.y, a*b.x}; }
template<class T> constexpr vec<T,2> cross (const vec<T,2> & a, T b) { return {a.y*b, -a.x*b}; }
template<class T> constexpr vec<T,3> cross (const vec<T,3> & a, const vec<T,3> & b) { return {a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x}; }
template<class T, int M> constexpr T dot (const vec<T,M> & a, const vec<T,M> & b) { return sum(a*b); }
template<class T, int M> constexpr T length2 (const vec<T,M> & a) { return dot(a,a); }
template<class T, int M> T length (const vec<T,M> & a) { return std::sqrt(length2(a)); }
template<class T, int M> vec<T,M> normalize(const vec<T,M> & a) { return a / length(a); }
template<class T, int M> constexpr T distance2(const vec<T,M> & a, const vec<T,M> & b) { return length2(b-a); }
template<class T, int M> T distance (const vec<T,M> & a, const vec<T,M> & b) { return length(b-a); }
template<class T, int M> T uangle (const vec<T,M> & a, const vec<T,M> & b) { T d=dot(a,b); return d > 1 ? 0 : std::acos(d < -1 ? -1 : d); }
template<class T, int M> T angle (const vec<T,M> & a, const vec<T,M> & b) { return uangle(normalize(a), normalize(b)); }
template<class T> vec<T,2> rot (T a, const vec<T,2> & v) { const T s = std::sin(a), c = std::cos(a); return {v.x*c - v.y*s, v.x*s + v.y*c}; }
template<class T, int M> vec<T,M> nlerp (const vec<T,M> & a, const vec<T,M> & b, T t) { return normalize(lerp(a,b,t)); }
template<class T, int M> vec<T,M> slerp (const vec<T,M> & a, const vec<T,M> & b, T t) { T th=uangle(a,b); return th == 0 ? a : a*(std::sin(th*(1-t))/std::sin(th)) + b*(std::sin(th*t)/std::sin(th)); }
// Support for quaternion algebra using 4D vectors, representing xi + yj + zk + w
template<class T> constexpr vec<T,4> qconj(const vec<T,4> & q) { return {-q.x,-q.y,-q.z,q.w}; }
template<class T> vec<T,4> qinv (const vec<T,4> & q) { return qconj(q)/length2(q); }
template<class T> vec<T,4> qexp (const vec<T,4> & q) { const auto v = q.xyz(); const auto vv = length(v); return std::exp(q.w) * vec<T,4>{v * (vv > 0 ? std::sin(vv)/vv : 0), std::cos(vv)}; }
template<class T> vec<T,4> qlog (const vec<T,4> & q) { const auto v = q.xyz(); const auto vv = length(v), qq = length(q); return {v * (vv > 0 ? std::acos(q.w/qq)/vv : 0), std::log(qq)}; }
template<class T> vec<T,4> qpow (const vec<T,4> & q, const T & p) { const auto v = q.xyz(); const auto vv = length(v), qq = length(q), th = std::acos(q.w/qq); return std::pow(qq,p)*vec<T,4>{v * (vv > 0 ? std::sin(p*th)/vv : 0), std::cos(p*th)}; }
template<class T> constexpr vec<T,4> qmul (const vec<T,4> & a, const vec<T,4> & b) { return {a.x*b.w+a.w*b.x+a.y*b.z-a.z*b.y, a.y*b.w+a.w*b.y+a.z*b.x-a.x*b.z, a.z*b.w+a.w*b.z+a.x*b.y-a.y*b.x, a.w*b.w-a.x*b.x-a.y*b.y-a.z*b.z}; }
template<class T, class... R> constexpr vec<T,4> qmul(const vec<T,4> & a, R... r) { return qmul(a, qmul(r...)); }
// Support for 3D spatial rotations using quaternions, via qmul(qmul(q, v), qconj(q))
template<class T> constexpr vec<T,3> qxdir (const vec<T,4> & q) { return {q.w*q.w+q.x*q.x-q.y*q.y-q.z*q.z, (q.x*q.y+q.z*q.w)*2, (q.z*q.x-q.y*q.w)*2}; }
template<class T> constexpr vec<T,3> qydir (const vec<T,4> & q) { return {(q.x*q.y-q.z*q.w)*2, q.w*q.w-q.x*q.x+q.y*q.y-q.z*q.z, (q.y*q.z+q.x*q.w)*2}; }
template<class T> constexpr vec<T,3> qzdir (const vec<T,4> & q) { return {(q.z*q.x+q.y*q.w)*2, (q.y*q.z-q.x*q.w)*2, q.w*q.w-q.x*q.x-q.y*q.y+q.z*q.z}; }
template<class T> constexpr mat<T,3,3> qmat (const vec<T,4> & q) { return {qxdir(q), qydir(q), qzdir(q)}; }
template<class T> constexpr vec<T,3> qrot (const vec<T,4> & q, const vec<T,3> & v) { return qxdir(q)*v.x + qydir(q)*v.y + qzdir(q)*v.z; }
template<class T> T qangle(const vec<T,4> & q) { return std::atan2(length(q.xyz()), q.w)*2; }
template<class T> vec<T,3> qaxis (const vec<T,4> & q) { return normalize(q.xyz()); }
template<class T> vec<T,4> qnlerp(const vec<T,4> & a, const vec<T,4> & b, T t) { return nlerp(a, dot(a,b) < 0 ? -b : b, t); }
template<class T> vec<T,4> qslerp(const vec<T,4> & a, const vec<T,4> & b, T t) { return slerp(a, dot(a,b) < 0 ? -b : b, t); }
// Support for matrix algebra
template<class T, int M> constexpr vec<T,M> mul(const mat<T,M,1> & a, const vec<T,1> & b) { return a.x*b.x; }
template<class T, int M> constexpr vec<T,M> mul(const mat<T,M,2> & a, const vec<T,2> & b) { return a.x*b.x + a.y*b.y; }
template<class T, int M> constexpr vec<T,M> mul(const mat<T,M,3> & a, const vec<T,3> & b) { return a.x*b.x + a.y*b.y + a.z*b.z; }
template<class T, int M> constexpr vec<T,M> mul(const mat<T,M,4> & a, const vec<T,4> & b) { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; }
template<class T, int M, int N> constexpr mat<T,M,1> mul(const mat<T,M,N> & a, const mat<T,N,1> & b) { return {mul(a,b.x)}; }
template<class T, int M, int N> constexpr mat<T,M,2> mul(const mat<T,M,N> & a, const mat<T,N,2> & b) { return {mul(a,b.x), mul(a,b.y)}; }
template<class T, int M, int N> constexpr mat<T,M,3> mul(const mat<T,M,N> & a, const mat<T,N,3> & b) { return {mul(a,b.x), mul(a,b.y), mul(a,b.z)}; }
template<class T, int M, int N> constexpr mat<T,M,4> mul(const mat<T,M,N> & a, const mat<T,N,4> & b) { return {mul(a,b.x), mul(a,b.y), mul(a,b.z), mul(a,b.w)}; }
template<class T, int M, int N, int P> constexpr vec<T,M> mul(const mat<T,M,N> & a, const mat<T,N,P> & b, const vec<T,P> & c) { return mul(mul(a,b),c); }
template<class T, int M, int N, int P, int Q> constexpr mat<T,M,Q> mul(const mat<T,M,N> & a, const mat<T,N,P> & b, const mat<T,P,Q> & c) { return mul(mul(a,b),c); }
template<class T, int M, int N, int P, int Q> constexpr vec<T,M> mul(const mat<T,M,N> & a, const mat<T,N,P> & b, const mat<T,P,Q> & c, const vec<T,Q> & d) { return mul(mul(a,b,c),d); }
template<class T, int M, int N, int P, int Q, int R> constexpr mat<T,M,R> mul(const mat<T,M,N> & a, const mat<T,N,P> & b, const mat<T,P,Q> & c, const mat<T,Q,R> & d) { return mul(mul(a,b,c),d); }
template<class T, int M> constexpr mat<T,M,1> outerprod(const vec<T,M> & a, const vec<T,1> & b) { return {a*b.x}; }
template<class T, int M> constexpr mat<T,M,2> outerprod(const vec<T,M> & a, const vec<T,2> & b) { return {a*b.x, a*b.y}; }
template<class T, int M> constexpr mat<T,M,3> outerprod(const vec<T,M> & a, const vec<T,3> & b) { return {a*b.x, a*b.y, a*b.z}; }
template<class T, int M> constexpr mat<T,M,4> outerprod(const vec<T,M> & a, const vec<T,4> & b) { return {a*b.x, a*b.y, a*b.z, a*b.w}; }
template<class T> constexpr vec<T,1> diagonal(const mat<T,1,1> & a) { return {a.x.x}; }
template<class T> constexpr vec<T,2> diagonal(const mat<T,2,2> & a) { return {a.x.x, a.y.y}; }
template<class T> constexpr vec<T,3> diagonal(const mat<T,3,3> & a) { return {a.x.x, a.y.y, a.z.z}; }
template<class T> constexpr vec<T,4> diagonal(const mat<T,4,4> & a) { return {a.x.x, a.y.y, a.z.z, a.w.w}; }
template<class T, int N> constexpr T trace(const mat<T,N,N> & a) { return sum(diagonal(a)); }
template<class T, int M> constexpr mat<T,M,1> transpose(const mat<T,1,M> & m) { return {m.row(0)}; }
template<class T, int M> constexpr mat<T,M,2> transpose(const mat<T,2,M> & m) { return {m.row(0), m.row(1)}; }
template<class T, int M> constexpr mat<T,M,3> transpose(const mat<T,3,M> & m) { return {m.row(0), m.row(1), m.row(2)}; }
template<class T, int M> constexpr mat<T,M,4> transpose(const mat<T,4,M> & m) { return {m.row(0), m.row(1), m.row(2), m.row(3)}; }
template<class T, int M> constexpr mat<T,1,M> transpose(const vec<T,M> & m) { return transpose(mat<T,M,1>(m)); }
template<class T> constexpr mat<T,1,1> adjugate(const mat<T,1,1> & a) { return {vec<T,1>{1}}; }
template<class T> constexpr mat<T,2,2> adjugate(const mat<T,2,2> & a) { return {{a.y.y, -a.x.y}, {-a.y.x, a.x.x}}; }
template<class T> constexpr mat<T,3,3> adjugate(const mat<T,3,3> & a);
template<class T> constexpr mat<T,4,4> adjugate(const mat<T,4,4> & a);
template<class T, int N> constexpr mat<T,N,N> comatrix(const mat<T,N,N> & a) { return transpose(adjugate(a)); }
template<class T> constexpr T determinant(const mat<T,1,1> & a) { return a.x.x; }
template<class T> constexpr T determinant(const mat<T,2,2> & a) { return a.x.x*a.y.y - a.x.y*a.y.x; }
template<class T> constexpr T determinant(const mat<T,3,3> & a) { return a.x.x*(a.y.y*a.z.z - a.z.y*a.y.z) + a.x.y*(a.y.z*a.z.x - a.z.z*a.y.x) + a.x.z*(a.y.x*a.z.y - a.z.x*a.y.y); }
template<class T> constexpr T determinant(const mat<T,4,4> & a);
template<class T, int N> constexpr mat<T,N,N> inverse(const mat<T,N,N> & a) { return adjugate(a)/determinant(a); }
// Vectors and matrices can be used as ranges
template<class T, int M> T * begin( vec<T,M> & a) { return &a.x; }
template<class T, int M> const T * begin(const vec<T,M> & a) { return &a.x; }
template<class T, int M> T * end ( vec<T,M> & a) { return begin(a) + M; }
template<class T, int M> const T * end (const vec<T,M> & a) { return begin(a) + M; }
template<class T, int M, int N> vec<T,M> * begin( mat<T,M,N> & a) { return &a.x; }
template<class T, int M, int N> const vec<T,M> * begin(const mat<T,M,N> & a) { return &a.x; }
template<class T, int M, int N> vec<T,M> * end ( mat<T,M,N> & a) { return begin(a) + N; }
template<class T, int M, int N> const vec<T,M> * end (const mat<T,M,N> & a) { return begin(a) + N; }
// Factory functions for 3D spatial transformations (will possibly be removed or changed in a future version)
enum fwd_axis { neg_z, pos_z }; // Should projection matrices be generated assuming forward is {0,0,-1} or {0,0,1}
enum z_range { neg_one_to_one, zero_to_one }; // Should projection matrices map z into the range of [-1,1] or [0,1]?
template<class T> vec<T,4> rotation_quat (const vec<T,3> & axis, T angle) { return {axis*std::sin(angle/2), std::cos(angle/2)}; }
template<class T> vec<T,4> rotation_quat (const mat<T,3,3> & m);
template<class T> mat<T,4,4> translation_matrix(const vec<T,3> & translation) { return {{1,0,0,0},{0,1,0,0},{0,0,1,0},{translation,1}}; }
template<class T> mat<T,4,4> rotation_matrix (const vec<T,4> & rotation) { return {{qxdir(rotation),0}, {qydir(rotation),0}, {qzdir(rotation),0}, {0,0,0,1}}; }
template<class T> mat<T,4,4> scaling_matrix (const vec<T,3> & scaling) { return {{scaling.x,0,0,0}, {0,scaling.y,0,0}, {0,0,scaling.z,0}, {0,0,0,1}}; }
template<class T> mat<T,4,4> pose_matrix (const vec<T,4> & q, const vec<T,3> & p) { return {{qxdir(q),0}, {qydir(q),0}, {qzdir(q),0}, {p,1}}; }
template<class T> mat<T,4,4> lookat_matrix (const vec<T,3> & eye, const vec<T,3> & center, const vec<T,3> & view_y_dir, fwd_axis fwd = neg_z);
template<class T> mat<T,4,4> frustum_matrix (T x0, T x1, T y0, T y1, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one);
template<class T> mat<T,4,4> perspective_matrix(T fovy, T aspect, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one) { T y = n*std::tan(fovy / 2), x = y*aspect; return frustum_matrix(-x, x, -y, y, n, f, a, z); }
// Provide implicit conversion between linalg::vec<T,M> and std::array<T,M>
template<class T> struct converter<vec<T,1>, std::array<T,1>> { vec<T,1> operator() (const std::array<T,1> & a) const { return {a[0]}; } };
template<class T> struct converter<vec<T,2>, std::array<T,2>> { vec<T,2> operator() (const std::array<T,2> & a) const { return {a[0], a[1]}; } };
template<class T> struct converter<vec<T,3>, std::array<T,3>> { vec<T,3> operator() (const std::array<T,3> & a) const { return {a[0], a[1], a[2]}; } };
template<class T> struct converter<vec<T,4>, std::array<T,4>> { vec<T,4> operator() (const std::array<T,4> & a) const { return {a[0], a[1], a[2], a[3]}; } };
template<class T> struct converter<std::array<T,1>, vec<T,1>> { std::array<T,1> operator() (const vec<T,1> & a) const { return {a[0]}; } };
template<class T> struct converter<std::array<T,2>, vec<T,2>> { std::array<T,2> operator() (const vec<T,2> & a) const { return {a[0], a[1]}; } };
template<class T> struct converter<std::array<T,3>, vec<T,3>> { std::array<T,3> operator() (const vec<T,3> & a) const { return {a[0], a[1], a[2]}; } };
template<class T> struct converter<std::array<T,4>, vec<T,4>> { std::array<T,4> operator() (const vec<T,4> & a) const { return {a[0], a[1], a[2], a[3]}; } };
// Provide typedefs for common element types and vector/matrix sizes
namespace aliases
{
typedef vec<bool,1> bool1; typedef vec<uint8_t,1> byte1; typedef vec<int16_t,1> short1; typedef vec<uint16_t,1> ushort1;
typedef vec<bool,2> bool2; typedef vec<uint8_t,2> byte2; typedef vec<int16_t,2> short2; typedef vec<uint16_t,2> ushort2;
typedef vec<bool,3> bool3; typedef vec<uint8_t,3> byte3; typedef vec<int16_t,3> short3; typedef vec<uint16_t,3> ushort3;
typedef vec<bool,4> bool4; typedef vec<uint8_t,4> byte4; typedef vec<int16_t,4> short4; typedef vec<uint16_t,4> ushort4;
typedef vec<int,1> int1; typedef vec<unsigned,1> uint1; typedef vec<float,1> float1; typedef vec<double,1> double1;
typedef vec<int,2> int2; typedef vec<unsigned,2> uint2; typedef vec<float,2> float2; typedef vec<double,2> double2;
typedef vec<int,3> int3; typedef vec<unsigned,3> uint3; typedef vec<float,3> float3; typedef vec<double,3> double3;
typedef vec<int,4> int4; typedef vec<unsigned,4> uint4; typedef vec<float,4> float4; typedef vec<double,4> double4;
typedef mat<bool,1,1> bool1x1; typedef mat<int,1,1> int1x1; typedef mat<float,1,1> float1x1; typedef mat<double,1,1> double1x1;
typedef mat<bool,1,2> bool1x2; typedef mat<int,1,2> int1x2; typedef mat<float,1,2> float1x2; typedef mat<double,1,2> double1x2;
typedef mat<bool,1,3> bool1x3; typedef mat<int,1,3> int1x3; typedef mat<float,1,3> float1x3; typedef mat<double,1,3> double1x3;
typedef mat<bool,1,4> bool1x4; typedef mat<int,1,4> int1x4; typedef mat<float,1,4> float1x4; typedef mat<double,1,4> double1x4;
typedef mat<bool,2,1> bool2x1; typedef mat<int,2,1> int2x1; typedef mat<float,2,1> float2x1; typedef mat<double,2,1> double2x1;
typedef mat<bool,2,2> bool2x2; typedef mat<int,2,2> int2x2; typedef mat<float,2,2> float2x2; typedef mat<double,2,2> double2x2;
typedef mat<bool,2,3> bool2x3; typedef mat<int,2,3> int2x3; typedef mat<float,2,3> float2x3; typedef mat<double,2,3> double2x3;
typedef mat<bool,2,4> bool2x4; typedef mat<int,2,4> int2x4; typedef mat<float,2,4> float2x4; typedef mat<double,2,4> double2x4;
typedef mat<bool,3,1> bool3x1; typedef mat<int,3,1> int3x1; typedef mat<float,3,1> float3x1; typedef mat<double,3,1> double3x1;
typedef mat<bool,3,2> bool3x2; typedef mat<int,3,2> int3x2; typedef mat<float,3,2> float3x2; typedef mat<double,3,2> double3x2;
typedef mat<bool,3,3> bool3x3; typedef mat<int,3,3> int3x3; typedef mat<float,3,3> float3x3; typedef mat<double,3,3> double3x3;
typedef mat<bool,3,4> bool3x4; typedef mat<int,3,4> int3x4; typedef mat<float,3,4> float3x4; typedef mat<double,3,4> double3x4;
typedef mat<bool,4,1> bool4x1; typedef mat<int,4,1> int4x1; typedef mat<float,4,1> float4x1; typedef mat<double,4,1> double4x1;
typedef mat<bool,4,2> bool4x2; typedef mat<int,4,2> int4x2; typedef mat<float,4,2> float4x2; typedef mat<double,4,2> double4x2;
typedef mat<bool,4,3> bool4x3; typedef mat<int,4,3> int4x3; typedef mat<float,4,3> float4x3; typedef mat<double,4,3> double4x3;
typedef mat<bool,4,4> bool4x4; typedef mat<int,4,4> int4x4; typedef mat<float,4,4> float4x4; typedef mat<double,4,4> double4x4;
}
// Provide output streaming operators, writing something that resembles an aggregate literal that could be used to construct the specified value
namespace ostream_overloads
{
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,1> & v) { return out << '{' << v[0] << '}'; }
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,2> & v) { return out << '{' << v[0] << ',' << v[1] << '}'; }
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,3> & v) { return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << '}'; }
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,4> & v) { return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << ',' << v[3] << '}'; }
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,1> & m) { return out << '{' << m[0] << '}'; }
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,2> & m) { return out << '{' << m[0] << ',' << m[1] << '}'; }
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,3> & m) { return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << '}'; }
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,4> & m) { return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << ',' << m[3] << '}'; }
}
}
namespace std
{
// Provide specializations for std::hash<...> with linalg types
template<class T> struct hash<linalg::vec<T,1>> { std::size_t operator()(const linalg::vec<T,1> & v) const { std::hash<T> h; return h(v.x); } };
template<class T> struct hash<linalg::vec<T,2>> { std::size_t operator()(const linalg::vec<T,2> & v) const { std::hash<T> h; return h(v.x) ^ (h(v.y) << 1); } };
template<class T> struct hash<linalg::vec<T,3>> { std::size_t operator()(const linalg::vec<T,3> & v) const { std::hash<T> h; return h(v.x) ^ (h(v.y) << 1) ^ (h(v.z) << 2); } };
template<class T> struct hash<linalg::vec<T,4>> { std::size_t operator()(const linalg::vec<T,4> & v) const { std::hash<T> h; return h(v.x) ^ (h(v.y) << 1) ^ (h(v.z) << 2) ^ (h(v.w) << 3); } };
template<class T, int M> struct hash<linalg::mat<T,M,1>> { std::size_t operator()(const linalg::mat<T,M,1> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x); } };
template<class T, int M> struct hash<linalg::mat<T,M,2>> { std::size_t operator()(const linalg::mat<T,M,2> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x) ^ (h(v.y) << M); } };
template<class T, int M> struct hash<linalg::mat<T,M,3>> { std::size_t operator()(const linalg::mat<T,M,3> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x) ^ (h(v.y) << M) ^ (h(v.z) << (M*2)); } };
template<class T, int M> struct hash<linalg::mat<T,M,4>> { std::size_t operator()(const linalg::mat<T,M,4> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x) ^ (h(v.y) << M) ^ (h(v.z) << (M*2)) ^ (h(v.w) << (M*3)); } };
}
// Definitions of functions too long to be defined inline
template<class T> constexpr linalg::mat<T,3,3> linalg::adjugate(const mat<T,3,3> & a)
{
return {{a.y.y*a.z.z - a.z.y*a.y.z, a.z.y*a.x.z - a.x.y*a.z.z, a.x.y*a.y.z - a.y.y*a.x.z},
{a.y.z*a.z.x - a.z.z*a.y.x, a.z.z*a.x.x - a.x.z*a.z.x, a.x.z*a.y.x - a.y.z*a.x.x},
{a.y.x*a.z.y - a.z.x*a.y.y, a.z.x*a.x.y - a.x.x*a.z.y, a.x.x*a.y.y - a.y.x*a.x.y}};
}
template<class T> constexpr linalg::mat<T,4,4> linalg::adjugate(const mat<T,4,4> & a)
{
return {{a.y.y*a.z.z*a.w.w + a.w.y*a.y.z*a.z.w + a.z.y*a.w.z*a.y.w - a.y.y*a.w.z*a.z.w - a.z.y*a.y.z*a.w.w - a.w.y*a.z.z*a.y.w,
a.x.y*a.w.z*a.z.w + a.z.y*a.x.z*a.w.w + a.w.y*a.z.z*a.x.w - a.w.y*a.x.z*a.z.w - a.z.y*a.w.z*a.x.w - a.x.y*a.z.z*a.w.w,
a.x.y*a.y.z*a.w.w + a.w.y*a.x.z*a.y.w + a.y.y*a.w.z*a.x.w - a.x.y*a.w.z*a.y.w - a.y.y*a.x.z*a.w.w - a.w.y*a.y.z*a.x.w,
a.x.y*a.z.z*a.y.w + a.y.y*a.x.z*a.z.w + a.z.y*a.y.z*a.x.w - a.x.y*a.y.z*a.z.w - a.z.y*a.x.z*a.y.w - a.y.y*a.z.z*a.x.w},
{a.y.z*a.w.w*a.z.x + a.z.z*a.y.w*a.w.x + a.w.z*a.z.w*a.y.x - a.y.z*a.z.w*a.w.x - a.w.z*a.y.w*a.z.x - a.z.z*a.w.w*a.y.x,
a.x.z*a.z.w*a.w.x + a.w.z*a.x.w*a.z.x + a.z.z*a.w.w*a.x.x - a.x.z*a.w.w*a.z.x - a.z.z*a.x.w*a.w.x - a.w.z*a.z.w*a.x.x,
a.x.z*a.w.w*a.y.x + a.y.z*a.x.w*a.w.x + a.w.z*a.y.w*a.x.x - a.x.z*a.y.w*a.w.x - a.w.z*a.x.w*a.y.x - a.y.z*a.w.w*a.x.x,
a.x.z*a.y.w*a.z.x + a.z.z*a.x.w*a.y.x + a.y.z*a.z.w*a.x.x - a.x.z*a.z.w*a.y.x - a.y.z*a.x.w*a.z.x - a.z.z*a.y.w*a.x.x},
{a.y.w*a.z.x*a.w.y + a.w.w*a.y.x*a.z.y + a.z.w*a.w.x*a.y.y - a.y.w*a.w.x*a.z.y - a.z.w*a.y.x*a.w.y - a.w.w*a.z.x*a.y.y,
a.x.w*a.w.x*a.z.y + a.z.w*a.x.x*a.w.y + a.w.w*a.z.x*a.x.y - a.x.w*a.z.x*a.w.y - a.w.w*a.x.x*a.z.y - a.z.w*a.w.x*a.x.y,
a.x.w*a.y.x*a.w.y + a.w.w*a.x.x*a.y.y + a.y.w*a.w.x*a.x.y - a.x.w*a.w.x*a.y.y - a.y.w*a.x.x*a.w.y - a.w.w*a.y.x*a.x.y,
a.x.w*a.z.x*a.y.y + a.y.w*a.x.x*a.z.y + a.z.w*a.y.x*a.x.y - a.x.w*a.y.x*a.z.y - a.z.w*a.x.x*a.y.y - a.y.w*a.z.x*a.x.y},
{a.y.x*a.w.y*a.z.z + a.z.x*a.y.y*a.w.z + a.w.x*a.z.y*a.y.z - a.y.x*a.z.y*a.w.z - a.w.x*a.y.y*a.z.z - a.z.x*a.w.y*a.y.z,
a.x.x*a.z.y*a.w.z + a.w.x*a.x.y*a.z.z + a.z.x*a.w.y*a.x.z - a.x.x*a.w.y*a.z.z - a.z.x*a.x.y*a.w.z - a.w.x*a.z.y*a.x.z,
a.x.x*a.w.y*a.y.z + a.y.x*a.x.y*a.w.z + a.w.x*a.y.y*a.x.z - a.x.x*a.y.y*a.w.z - a.w.x*a.x.y*a.y.z - a.y.x*a.w.y*a.x.z,
a.x.x*a.y.y*a.z.z + a.z.x*a.x.y*a.y.z + a.y.x*a.z.y*a.x.z - a.x.x*a.z.y*a.y.z - a.y.x*a.x.y*a.z.z - a.z.x*a.y.y*a.x.z}};
}
template<class T> constexpr T linalg::determinant(const mat<T,4,4> & a)
{
return a.x.x*(a.y.y*a.z.z*a.w.w + a.w.y*a.y.z*a.z.w + a.z.y*a.w.z*a.y.w - a.y.y*a.w.z*a.z.w - a.z.y*a.y.z*a.w.w - a.w.y*a.z.z*a.y.w)
+ a.x.y*(a.y.z*a.w.w*a.z.x + a.z.z*a.y.w*a.w.x + a.w.z*a.z.w*a.y.x - a.y.z*a.z.w*a.w.x - a.w.z*a.y.w*a.z.x - a.z.z*a.w.w*a.y.x)
+ a.x.z*(a.y.w*a.z.x*a.w.y + a.w.w*a.y.x*a.z.y + a.z.w*a.w.x*a.y.y - a.y.w*a.w.x*a.z.y - a.z.w*a.y.x*a.w.y - a.w.w*a.z.x*a.y.y)
+ a.x.w*(a.y.x*a.w.y*a.z.z + a.z.x*a.y.y*a.w.z + a.w.x*a.z.y*a.y.z - a.y.x*a.z.y*a.w.z - a.w.x*a.y.y*a.z.z - a.z.x*a.w.y*a.y.z);
}
template<class T> linalg::vec<T,4> linalg::rotation_quat(const mat<T,3,3> & m)
{
const vec<T,4> q {m.x.x-m.y.y-m.z.z, m.y.y-m.x.x-m.z.z, m.z.z-m.x.x-m.y.y, m.x.x+m.y.y+m.z.z}, s[] {
{1, m.x.y + m.y.x, m.z.x + m.x.z, m.y.z - m.z.y},
{m.x.y + m.y.x, 1, m.y.z + m.z.y, m.z.x - m.x.z},
{m.x.z + m.z.x, m.y.z + m.z.y, 1, m.x.y - m.y.x},
{m.y.z - m.z.y, m.z.x - m.x.z, m.x.y - m.y.x, 1}};
return copysign(normalize(sqrt(max(T(0), T(1)+q))), s[argmax(q)]);
}
template<class T> linalg::mat<T,4,4> linalg::lookat_matrix(const vec<T,3> & eye, const vec<T,3> & center, const vec<T,3> & view_y_dir, fwd_axis a)
{
const vec<T,3> f = normalize(center - eye), z = a == pos_z ? f : -f, x = normalize(cross(view_y_dir, z)), y = cross(z, x);
return inverse(mat<T,4,4>{{x,0},{y,0},{z,0},{eye,1}});
}
template<class T> linalg::mat<T,4,4> linalg::frustum_matrix(T x0, T x1, T y0, T y1, T n, T f, fwd_axis a, z_range z)
{
const T s = a == pos_z ? T(1) : T(-1), o = z == neg_one_to_one ? n : 0;
return {{2*n/(x1-x0),0,0,0}, {0,2*n/(y1-y0),0,0}, {-s*(x0+x1)/(x1-x0),-s*(y0+y1)/(y1-y0),s*(f+o)/(f-n),s}, {0,0,-(n+o)*f/(f-n),0}};
}
#endif

View File

@ -0,0 +1,27 @@
## Earcut.hpp changelog
### master
- Fixed a bunch of rare edge cases that led to bad triangulation (parity with Earcut v2.2.2)
- Removed use of deprecated `std::allocator::construct`
- Fixed a minor z-order hashing bug
- Improved visualization app, better docs
### v0.12.4
- Fixed a crash in Crash in Earcut::findHoleBridge
- Added coverage checks
- Added macOS, MinGW builds
### v0.12.3
- Fixed -Wunused-lambda-capture
### v0.12.2
- Fixed potential division by zero
- Fixed -fsanitize=integer warning
### v0.12.1
- Fixed cast precision warning

View File

@ -0,0 +1,15 @@
ISC License
Copyright (c) 2015, Mapbox
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.

View File

@ -0,0 +1,131 @@
## Earcut
A C++ port of [earcut.js](https://github.com/mapbox/earcut), a fast, [header-only](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) polygon triangulation library.
[![Travis](https://img.shields.io/travis/com/mapbox/earcut.hpp.svg)](https://travis-ci.com/github/mapbox/earcut.hpp)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/a1ysrqd69mqn7coo/branch/master?svg=true)](https://ci.appveyor.com/project/Mapbox/earcut-hpp-8wm4o/branch/master)
[![Coverage](https://img.shields.io/coveralls/github/mapbox/earcut.hpp.svg)](https://coveralls.io/github/mapbox/earcut.hpp)
[![Coverity Scan](https://img.shields.io/coverity/scan/14000.svg)](https://scan.coverity.com/projects/14000)
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Average time to resolve an issue")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Percentage of issues still open")
[![Mourner](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects)
The library implements a modified ear slicing algorithm, optimized by [z-order curve](http://en.wikipedia.org/wiki/Z-order_curve) hashing and extended to handle holes, twisted polygons, degeneracies and self-intersections in a way that doesn't _guarantee_ correctness of triangulation, but attempts to always produce acceptable results for practical data like geographical shapes.
It's based on ideas from [FIST: Fast Industrial-Strength Triangulation of Polygons](http://www.cosy.sbg.ac.at/~held/projects/triang/triang.html) by Martin Held and [Triangulation by Ear Clipping](http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf) by David Eberly.
## Usage
```cpp
#include <earcut.hpp>
```
```cpp
// The number type to use for tessellation
using Coord = double;
// The index type. Defaults to uint32_t, but you can also pass uint16_t if you know that your
// data won't have more than 65536 vertices.
using N = uint32_t;
// Create array
using Point = std::array<Coord, 2>;
std::vector<std::vector<Point>> polygon;
// Fill polygon structure with actual data. Any winding order works.
// The first polyline defines the main polygon.
polygon.push_back({{100, 0}, {100, 100}, {0, 100}, {0, 0}});
// Following polylines define holes.
polygon.push_back({{75, 25}, {75, 75}, {25, 75}, {25, 25}});
// Run tessellation
// Returns array of indices that refer to the vertices of the input polygon.
// e.g: the index 6 would refer to {25, 75} in this example.
// Three subsequent indices form a triangle. Output triangles are clockwise.
std::vector<N> indices = mapbox::earcut<N>(polygon);
```
Earcut can triangulate a simple, planar polygon of any winding order including holes. It will even return a robust, acceptable solution for non-simple poygons. Earcut works on a 2D plane. If you have three or more dimensions, you can project them onto a 2D surface before triangulation, or use a more suitable library for the task (e.g [CGAL](https://doc.cgal.org/latest/Triangulation_3/index.html)).
It is also possible to use your custom point type as input. There are default accessors defined for `std::tuple`, `std::pair`, and `std::array`. For a custom type (like Clipper's `IntPoint` type), do this:
```cpp
// struct IntPoint {
// int64_t X, Y;
// };
namespace mapbox {
namespace util {
template <>
struct nth<0, IntPoint> {
inline static auto get(const IntPoint &t) {
return t.X;
};
};
template <>
struct nth<1, IntPoint> {
inline static auto get(const IntPoint &t) {
return t.Y;
};
};
} // namespace util
} // namespace mapbox
```
You can also use a custom container type for your polygon. Similar to std::vector<T>, it has to meet the requirements of [Container](https://en.cppreference.com/w/cpp/named_req/Container), in particular `size()`, `empty()` and `operator[]`.
<p align="center">
<img src="https://camo.githubusercontent.com/01836f8ba21af844c93d8d3145f4e9976025a696/68747470733a2f2f692e696d6775722e636f6d2f67314e704c54712e706e67" alt="example triangulation"/>
</p>
## Additional build instructions
In case you just want to use the earcut triangulation library; copy and include the header file [`<earcut.hpp>`](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) in your project and follow the steps documented in the section [Usage](#usage).
If you want to build the test, benchmark and visualization programs instead, follow these instructions:
### Dependencies
Before you continue, make sure to have the following tools and libraries installed:
* git ([Ubuntu](https://help.ubuntu.com/lts/serverguide/git.html)/[Windows/macOS](http://git-scm.com/downloads))
* cmake 3.2+ ([Ubuntu](https://launchpad.net/~george-edison55/+archive/ubuntu/cmake-3.x)/[Windows/macOS](https://cmake.org/download/))
* OpenGL SDK ([Ubuntu](http://packages.ubuntu.com/de/trusty/libgl1-mesa-dev)/[Windows](https://dev.windows.com/en-us/downloads/windows-10-sdk)/[macOS](https://developer.apple.com/opengl/))
* Compiler such as [GCC 4.9+, Clang 3.4+](https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test), [MSVC12+](https://www.visualstudio.com/)
Note: On some operating systems such as Windows, manual steps are required to add cmake and [git](http://blog.countableset.ch/2012/06/07/adding-git-to-windows-7-path/) to your PATH environment variable.
### Manual compilation
```bash
git clone --recursive https://github.com/mapbox/earcut.hpp.git
cd earcut.hpp
mkdir build
cd build
cmake ..
make
# ./tests
# ./bench
# ./viz
```
### [Visual Studio](https://www.visualstudio.com/), [Eclipse](https://eclipse.org/), [XCode](https://developer.apple.com/xcode/), ...
```batch
git clone --recursive https://github.com/mapbox/earcut.hpp.git
cd earcut.hpp
mkdir project
cd project
cmake .. -G "Visual Studio 14 2015"
::you can also generate projects for "Visual Studio 12 2013", "XCode", "Eclipse CDT4 - Unix Makefiles"
```
After completion, open the generated project with your IDE.
### [CLion](https://www.jetbrains.com/clion/), [Visual Studio 2017+](https://www.visualstudio.com/)
Import the project from https://github.com/mapbox/earcut.hpp.git and you should be good to go!
## Status
This is currently based on [earcut 2.2.4](https://github.com/mapbox/earcut#224-jul-5-2022).

View File

@ -0,0 +1,816 @@
#pragma once
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <limits>
#include <memory>
#include <utility>
#include <vector>
namespace mapbox {
namespace util {
template <std::size_t I, typename T> struct nth {
inline static typename std::tuple_element<I, T>::type
get(const T& t) { return std::get<I>(t); };
};
}
namespace detail {
template <typename N = uint32_t>
class Earcut {
public:
std::vector<N> indices;
std::size_t vertices = 0;
template <typename Polygon>
void operator()(const Polygon& points);
private:
struct Node {
Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {}
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
Node(Node&&) = delete;
Node& operator=(Node&&) = delete;
const N i;
const double x;
const double y;
// previous and next vertice nodes in a polygon ring
Node* prev = nullptr;
Node* next = nullptr;
// z-order curve value
int32_t z = 0;
// previous and next nodes in z-order
Node* prevZ = nullptr;
Node* nextZ = nullptr;
// indicates whether this is a steiner point
bool steiner = false;
};
template <typename Ring> Node* linkedList(const Ring& points, const bool clockwise);
Node* filterPoints(Node* start, Node* end = nullptr);
void earcutLinked(Node* ear, int pass = 0);
bool isEar(Node* ear);
bool isEarHashed(Node* ear);
Node* cureLocalIntersections(Node* start);
void splitEarcut(Node* start);
template <typename Polygon> Node* eliminateHoles(const Polygon& points, Node* outerNode);
Node* eliminateHole(Node* hole, Node* outerNode);
Node* findHoleBridge(Node* hole, Node* outerNode);
bool sectorContainsSector(const Node* m, const Node* p);
void indexCurve(Node* start);
Node* sortLinked(Node* list);
int32_t zOrder(const double x_, const double y_);
Node* getLeftmost(Node* start);
bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const;
bool isValidDiagonal(Node* a, Node* b);
double area(const Node* p, const Node* q, const Node* r) const;
bool equals(const Node* p1, const Node* p2);
bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2);
bool onSegment(const Node* p, const Node* q, const Node* r);
int sign(double val);
bool intersectsPolygon(const Node* a, const Node* b);
bool locallyInside(const Node* a, const Node* b);
bool middleInside(const Node* a, const Node* b);
Node* splitPolygon(Node* a, Node* b);
template <typename Point> Node* insertNode(std::size_t i, const Point& p, Node* last);
void removeNode(Node* p);
bool hashing;
double minX, maxX;
double minY, maxY;
double inv_size = 0;
template <typename T, typename Alloc = std::allocator<T>>
class ObjectPool {
public:
ObjectPool() { }
ObjectPool(std::size_t blockSize_) {
reset(blockSize_);
}
~ObjectPool() {
clear();
}
template <typename... Args>
T* construct(Args&&... args) {
if (currentIndex >= blockSize) {
currentBlock = alloc_traits::allocate(alloc, blockSize);
allocations.emplace_back(currentBlock);
currentIndex = 0;
}
T* object = &currentBlock[currentIndex++];
alloc_traits::construct(alloc, object, std::forward<Args>(args)...);
return object;
}
void reset(std::size_t newBlockSize) {
for (auto allocation : allocations) {
alloc_traits::deallocate(alloc, allocation, blockSize);
}
allocations.clear();
blockSize = std::max<std::size_t>(1, newBlockSize);
currentBlock = nullptr;
currentIndex = blockSize;
}
void clear() { reset(blockSize); }
private:
T* currentBlock = nullptr;
std::size_t currentIndex = 1;
std::size_t blockSize = 1;
std::vector<T*> allocations;
Alloc alloc;
typedef typename std::allocator_traits<Alloc> alloc_traits;
};
ObjectPool<Node> nodes;
};
template <typename N> template <typename Polygon>
void Earcut<N>::operator()(const Polygon& points) {
// reset
indices.clear();
vertices = 0;
if (points.empty()) return;
double x;
double y;
int threshold = 80;
std::size_t len = 0;
for (size_t i = 0; threshold >= 0 && i < points.size(); i++) {
threshold -= static_cast<int>(points[i].size());
len += points[i].size();
}
//estimate size of nodes and indices
nodes.reset(len * 3 / 2);
indices.reserve(len + points[0].size());
Node* outerNode = linkedList(points[0], true);
if (!outerNode || outerNode->prev == outerNode->next) return;
if (points.size() > 1) outerNode = eliminateHoles(points, outerNode);
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
hashing = threshold < 0;
if (hashing) {
Node* p = outerNode->next;
minX = maxX = outerNode->x;
minY = maxY = outerNode->y;
do {
x = p->x;
y = p->y;
minX = std::min<double>(minX, x);
minY = std::min<double>(minY, y);
maxX = std::max<double>(maxX, x);
maxY = std::max<double>(maxY, y);
p = p->next;
} while (p != outerNode);
// minX, minY and inv_size are later used to transform coords into integers for z-order calculation
inv_size = std::max<double>(maxX - minX, maxY - minY);
inv_size = inv_size != .0 ? (32767. / inv_size) : .0;
}
earcutLinked(outerNode);
nodes.clear();
}
// create a circular doubly linked list from polygon points in the specified winding order
template <typename N> template <typename Ring>
typename Earcut<N>::Node*
Earcut<N>::linkedList(const Ring& points, const bool clockwise) {
using Point = typename Ring::value_type;
double sum = 0;
const std::size_t len = points.size();
std::size_t i, j;
Node* last = nullptr;
// calculate original winding order of a polygon ring
for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) {
const auto& p1 = points[i];
const auto& p2 = points[j];
const double p20 = util::nth<0, Point>::get(p2);
const double p10 = util::nth<0, Point>::get(p1);
const double p11 = util::nth<1, Point>::get(p1);
const double p21 = util::nth<1, Point>::get(p2);
sum += (p20 - p10) * (p11 + p21);
}
// link points into circular doubly-linked list in the specified winding order
if (clockwise == (sum > 0)) {
for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last);
} else {
for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last);
}
if (last && equals(last, last->next)) {
removeNode(last);
last = last->next;
}
vertices += len;
return last;
}
// eliminate colinear or duplicate points
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::filterPoints(Node* start, Node* end) {
if (!end) end = start;
Node* p = start;
bool again;
do {
again = false;
if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) {
removeNode(p);
p = end = p->prev;
if (p == p->next) break;
again = true;
} else {
p = p->next;
}
} while (again || p != end);
return end;
}
// main ear slicing loop which triangulates a polygon (given as a linked list)
template <typename N>
void Earcut<N>::earcutLinked(Node* ear, int pass) {
if (!ear) return;
// interlink polygon nodes in z-order
if (!pass && hashing) indexCurve(ear);
Node* stop = ear;
Node* prev;
Node* next;
int iterations = 0;
// iterate through ears, slicing them one by one
while (ear->prev != ear->next) {
iterations++;
prev = ear->prev;
next = ear->next;
if (hashing ? isEarHashed(ear) : isEar(ear)) {
// cut off the triangle
indices.emplace_back(prev->i);
indices.emplace_back(ear->i);
indices.emplace_back(next->i);
removeNode(ear);
// skipping the next vertice leads to less sliver triangles
ear = next->next;
stop = next->next;
continue;
}
ear = next;
// if we looped through the whole remaining polygon and can't find any more ears
if (ear == stop) {
// try filtering points and slicing again
if (!pass) earcutLinked(filterPoints(ear), 1);
// if this didn't work, try curing all small self-intersections locally
else if (pass == 1) {
ear = cureLocalIntersections(filterPoints(ear));
earcutLinked(ear, 2);
// as a last resort, try splitting the remaining polygon into two
} else if (pass == 2) splitEarcut(ear);
break;
}
}
}
// check whether a polygon node forms a valid ear with adjacent nodes
template <typename N>
bool Earcut<N>::isEar(Node* ear) {
const Node* a = ear->prev;
const Node* b = ear;
const Node* c = ear->next;
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
// now make sure we don't have other points inside the potential ear
Node* p = ear->next->next;
while (p != ear->prev) {
if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
area(p->prev, p, p->next) >= 0) return false;
p = p->next;
}
return true;
}
template <typename N>
bool Earcut<N>::isEarHashed(Node* ear) {
const Node* a = ear->prev;
const Node* b = ear;
const Node* c = ear->next;
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
// triangle bbox; min & max are calculated like this for speed
const double minTX = std::min<double>(a->x, std::min<double>(b->x, c->x));
const double minTY = std::min<double>(a->y, std::min<double>(b->y, c->y));
const double maxTX = std::max<double>(a->x, std::max<double>(b->x, c->x));
const double maxTY = std::max<double>(a->y, std::max<double>(b->y, c->y));
// z-order range for the current triangle bbox;
const int32_t minZ = zOrder(minTX, minTY);
const int32_t maxZ = zOrder(maxTX, maxTY);
// first look for points inside the triangle in increasing z-order
Node* p = ear->nextZ;
while (p && p->z <= maxZ) {
if (p != ear->prev && p != ear->next &&
pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
area(p->prev, p, p->next) >= 0) return false;
p = p->nextZ;
}
// then look for points in decreasing z-order
p = ear->prevZ;
while (p && p->z >= minZ) {
if (p != ear->prev && p != ear->next &&
pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
area(p->prev, p, p->next) >= 0) return false;
p = p->prevZ;
}
return true;
}
// go through all polygon nodes and cure small local self-intersections
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::cureLocalIntersections(Node* start) {
Node* p = start;
do {
Node* a = p->prev;
Node* b = p->next->next;
// a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) {
indices.emplace_back(a->i);
indices.emplace_back(p->i);
indices.emplace_back(b->i);
// remove two nodes involved
removeNode(p);
removeNode(p->next);
p = start = b;
}
p = p->next;
} while (p != start);
return filterPoints(p);
}
// try splitting polygon into two and triangulate them independently
template <typename N>
void Earcut<N>::splitEarcut(Node* start) {
// look for a valid diagonal that divides the polygon into two
Node* a = start;
do {
Node* b = a->next->next;
while (b != a->prev) {
if (a->i != b->i && isValidDiagonal(a, b)) {
// split the polygon in two by the diagonal
Node* c = splitPolygon(a, b);
// filter colinear points around the cuts
a = filterPoints(a, a->next);
c = filterPoints(c, c->next);
// run earcut on each half
earcutLinked(a);
earcutLinked(c);
return;
}
b = b->next;
}
a = a->next;
} while (a != start);
}
// link every hole into the outer loop, producing a single-ring polygon without holes
template <typename N> template <typename Polygon>
typename Earcut<N>::Node*
Earcut<N>::eliminateHoles(const Polygon& points, Node* outerNode) {
const size_t len = points.size();
std::vector<Node*> queue;
for (size_t i = 1; i < len; i++) {
Node* list = linkedList(points[i], false);
if (list) {
if (list == list->next) list->steiner = true;
queue.push_back(getLeftmost(list));
}
}
std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) {
return a->x < b->x;
});
// process holes from left to right
for (size_t i = 0; i < queue.size(); i++) {
outerNode = eliminateHole(queue[i], outerNode);
}
return outerNode;
}
// find a bridge between vertices that connects hole with an outer ring and and link it
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::eliminateHole(Node* hole, Node* outerNode) {
Node* bridge = findHoleBridge(hole, outerNode);
if (!bridge) {
return outerNode;
}
Node* bridgeReverse = splitPolygon(bridge, hole);
// filter collinear points around the cuts
filterPoints(bridgeReverse, bridgeReverse->next);
// Check if input node was removed by the filtering
return filterPoints(bridge, bridge->next);
}
// David Eberly's algorithm for finding a bridge between hole and outer polygon
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::findHoleBridge(Node* hole, Node* outerNode) {
Node* p = outerNode;
double hx = hole->x;
double hy = hole->y;
double qx = -std::numeric_limits<double>::infinity();
Node* m = nullptr;
// find a segment intersected by a ray from the hole's leftmost Vertex to the left;
// segment's endpoint with lesser x will be potential connection Vertex
do {
if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) {
double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y);
if (x <= hx && x > qx) {
qx = x;
m = p->x < p->next->x ? p : p->next;
if (x == hx) return m; // hole touches outer segment; pick leftmost endpoint
}
}
p = p->next;
} while (p != outerNode);
if (!m) return 0;
// look for points inside the triangle of hole Vertex, segment intersection and endpoint;
// if there are no points found, we have a valid connection;
// otherwise choose the Vertex of the minimum angle with the ray as connection Vertex
const Node* stop = m;
double tanMin = std::numeric_limits<double>::infinity();
double tanCur = 0;
p = m;
double mx = m->x;
double my = m->y;
do {
if (hx >= p->x && p->x >= mx && hx != p->x &&
pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) {
tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential
if (locallyInside(p, hole) &&
(tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) {
m = p;
tanMin = tanCur;
}
}
p = p->next;
} while (p != stop);
return m;
}
// whether sector in vertex m contains sector in vertex p in the same coordinates
template <typename N>
bool Earcut<N>::sectorContainsSector(const Node* m, const Node* p) {
return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0;
}
// interlink polygon nodes in z-order
template <typename N>
void Earcut<N>::indexCurve(Node* start) {
assert(start);
Node* p = start;
do {
p->z = p->z ? p->z : zOrder(p->x, p->y);
p->prevZ = p->prev;
p->nextZ = p->next;
p = p->next;
} while (p != start);
p->prevZ->nextZ = nullptr;
p->prevZ = nullptr;
sortLinked(p);
}
// Simon Tatham's linked list merge sort algorithm
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::sortLinked(Node* list) {
assert(list);
Node* p;
Node* q;
Node* e;
Node* tail;
int i, numMerges, pSize, qSize;
int inSize = 1;
for (;;) {
p = list;
list = nullptr;
tail = nullptr;
numMerges = 0;
while (p) {
numMerges++;
q = p;
pSize = 0;
for (i = 0; i < inSize; i++) {
pSize++;
q = q->nextZ;
if (!q) break;
}
qSize = inSize;
while (pSize > 0 || (qSize > 0 && q)) {
if (pSize == 0) {
e = q;
q = q->nextZ;
qSize--;
} else if (qSize == 0 || !q) {
e = p;
p = p->nextZ;
pSize--;
} else if (p->z <= q->z) {
e = p;
p = p->nextZ;
pSize--;
} else {
e = q;
q = q->nextZ;
qSize--;
}
if (tail) tail->nextZ = e;
else list = e;
e->prevZ = tail;
tail = e;
}
p = q;
}
tail->nextZ = nullptr;
if (numMerges <= 1) return list;
inSize *= 2;
}
}
// z-order of a Vertex given coords and size of the data bounding box
template <typename N>
int32_t Earcut<N>::zOrder(const double x_, const double y_) {
// coords are transformed into non-negative 15-bit integer range
int32_t x = static_cast<int32_t>((x_ - minX) * inv_size);
int32_t y = static_cast<int32_t>((y_ - minY) * inv_size);
x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F;
x = (x | (x << 2)) & 0x33333333;
x = (x | (x << 1)) & 0x55555555;
y = (y | (y << 8)) & 0x00FF00FF;
y = (y | (y << 4)) & 0x0F0F0F0F;
y = (y | (y << 2)) & 0x33333333;
y = (y | (y << 1)) & 0x55555555;
return x | (y << 1);
}
// find the leftmost node of a polygon ring
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::getLeftmost(Node* start) {
Node* p = start;
Node* leftmost = start;
do {
if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y))
leftmost = p;
p = p->next;
} while (p != start);
return leftmost;
}
// check if a point lies within a convex triangle
template <typename N>
bool Earcut<N>::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const {
return (cx - px) * (ay - py) >= (ax - px) * (cy - py) &&
(ax - px) * (by - py) >= (bx - px) * (ay - py) &&
(bx - px) * (cy - py) >= (cx - px) * (by - py);
}
// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
template <typename N>
bool Earcut<N>::isValidDiagonal(Node* a, Node* b) {
return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges
((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
(area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors
(equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case
}
// signed area of a triangle
template <typename N>
double Earcut<N>::area(const Node* p, const Node* q, const Node* r) const {
return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y);
}
// check if two points are equal
template <typename N>
bool Earcut<N>::equals(const Node* p1, const Node* p2) {
return p1->x == p2->x && p1->y == p2->y;
}
// check if two segments intersect
template <typename N>
bool Earcut<N>::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) {
int o1 = sign(area(p1, q1, p2));
int o2 = sign(area(p1, q1, q2));
int o3 = sign(area(p2, q2, p1));
int o4 = sign(area(p2, q2, q1));
if (o1 != o2 && o3 != o4) return true; // general case
if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
return false;
}
// for collinear points p, q, r, check if point q lies on segment pr
template <typename N>
bool Earcut<N>::onSegment(const Node* p, const Node* q, const Node* r) {
return q->x <= std::max<double>(p->x, r->x) &&
q->x >= std::min<double>(p->x, r->x) &&
q->y <= std::max<double>(p->y, r->y) &&
q->y >= std::min<double>(p->y, r->y);
}
template <typename N>
int Earcut<N>::sign(double val) {
return (0.0 < val) - (val < 0.0);
}
// check if a polygon diagonal intersects any polygon segments
template <typename N>
bool Earcut<N>::intersectsPolygon(const Node* a, const Node* b) {
const Node* p = a;
do {
if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i &&
intersects(p, p->next, a, b)) return true;
p = p->next;
} while (p != a);
return false;
}
// check if a polygon diagonal is locally inside the polygon
template <typename N>
bool Earcut<N>::locallyInside(const Node* a, const Node* b) {
return area(a->prev, a, a->next) < 0 ?
area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 :
area(a, b, a->prev) < 0 || area(a, a->next, b) < 0;
}
// check if the middle Vertex of a polygon diagonal is inside the polygon
template <typename N>
bool Earcut<N>::middleInside(const Node* a, const Node* b) {
const Node* p = a;
bool inside = false;
double px = (a->x + b->x) / 2;
double py = (a->y + b->y) / 2;
do {
if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y &&
(px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x))
inside = !inside;
p = p->next;
} while (p != a);
return inside;
}
// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits
// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a
// single ring
template <typename N>
typename Earcut<N>::Node*
Earcut<N>::splitPolygon(Node* a, Node* b) {
Node* a2 = nodes.construct(a->i, a->x, a->y);
Node* b2 = nodes.construct(b->i, b->x, b->y);
Node* an = a->next;
Node* bp = b->prev;
a->next = b;
b->prev = a;
a2->next = an;
an->prev = a2;
b2->next = a2;
a2->prev = b2;
bp->next = b2;
b2->prev = bp;
return b2;
}
// create a node and util::optionally link it with previous one (in a circular doubly linked list)
template <typename N> template <typename Point>
typename Earcut<N>::Node*
Earcut<N>::insertNode(std::size_t i, const Point& pt, Node* last) {
Node* p = nodes.construct(static_cast<N>(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt));
if (!last) {
p->prev = p;
p->next = p;
} else {
assert(last);
p->next = last->next;
p->prev = last;
last->next->prev = p;
last->next = p;
}
return p;
}
template <typename N>
void Earcut<N>::removeNode(Node* p) {
p->next->prev = p->prev;
p->prev->next = p->next;
if (p->prevZ) p->prevZ->nextZ = p->nextZ;
if (p->nextZ) p->nextZ->prevZ = p->prevZ;
}
}
template <typename N = uint32_t, typename Polygon>
std::vector<N> earcut(const Polygon& poly) {
mapbox::detail::Earcut<N> earcut;
earcut(poly);
return std::move(earcut.indices);
}
}

View File

@ -0,0 +1,15 @@
ISC License
Copyright (c) 2018, Mapbox
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.

View File

@ -0,0 +1,80 @@
`eternal.hpp` is a header-only C++ implementation of `constexpr`/compile-time maps and hash maps. It provides an API that is somewhat compatible with `std::map`/`std::unordered_map`, but doesn't support insertion, or other modifications. It's main focus is in **binary size**: it generates minimal code and doesn't incur any static initialization overhead.
**Why is this useful?**
- Lookup tables
**Tested with these compilers/platforms:**
- *Linux GCC 4.9.4* (runtime only, since `constexpr` support is broken in this version)
- Linux GCC 5.5
- Linux GCC 6.5
- Linux GCC 7.3
- Linux GCC 8.1
- Linux Clang 3.9.1
- Linux Clang 4
- Linux Clang 5
- Linux Clang 6
- Linux Clang 7
- macOS Xcode 10.1
- Android NDK r17+
## Usage
```cpp
MAPBOX_ETERNAL_CONSTEXPR const auto colors = mapbox::eternal::map<mapbox::eternal::string, Color>({
{ "red", { 255, 0, 0, 1 } },
{ "green", { 0, 128, 0, 1 } },
{ "yellow", { 255, 255, 0, 1 } },
{ "white", { 255, 255, 255, 1 } },
{ "black", { 0, 0, 0, 1 } }
});
```
- `mapbox::eternal::map<key, value>()` is a factory function that produces a `constexpr` map from the `std::pair<key, value>`s passed to it.
- Alternatively, use `mapbox::eternal::hash_map<key, value>()` to construct a hash map. The `key` needs a specialization of `std::hash`.
- Use `mapbox::eternal::string` for `constexpr` strings.
- If you need to support GCC 4.9, use `MAPBOX_ETERNAL_CONSTEXPR` instead of `constexpr` in the variable definition.
- You can pass the elements in arbitrary order; they will be sorted at compile time (except for GCC 4.9). To speed up compilation, list the elements in sorted order. To determine the sort order for `hash_map`, iterate over the map and print the result. Note that hashes may be architecture-specific.
- Both `map()` and `hash_map()` support multiple values for the same key. To ensure that keys are unique, run `static_assert(map.unique());` (or equivalent for GCC 4.9)
- The preprocessor variable `MAPBOX_ETERNAL_IS_CONSTEXPR` is set to `1` if `constexpr` construction and lookups are supported.
## Performance
Uses a list of 148 unique CSS color names
```
Run on (8 X 2600 MHz CPU s)
CPU Caches:
L1 Data 32K (x4)
L1 Instruction 32K (x4)
L2 Unified 262K (x4)
L3 Unified 6291K (x1)
-----------------------------------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------------------------------
EternalMap_ConstexprLookup 2 ns 2 ns 424582090
EternalMap_Lookup 48 ns 48 ns 14494194
EternalMap_LookupMissing 35 ns 35 ns 20442492
EternalMap_LookupEqualRange 78 ns 78 ns 8862891
EternalHashMap_ConstexprLookup 2 ns 2 ns 429076688
EternalHashMap_Lookup 30 ns 30 ns 22685952
EternalHashMap_LookupMissing 17 ns 17 ns 39521898
EternalHashMap_LookupEqualRange 43 ns 43 ns 16696442
StdMap_Lookup 51 ns 50 ns 13580630
StdMap_LookupMissing 58 ns 58 ns 11868028
StdMap_LookupEqualRange 89 ns 89 ns 7766129
StdMultimap_Lookup 50 ns 50 ns 14262312
StdMultimap_LookupMissing 60 ns 59 ns 11555922
StdMultimap_LookupEqualRange 103 ns 103 ns 6783077
StdUnorderedMap_Lookup 43 ns 43 ns 16175696
StdUnorderedMap_LookupMissing 50 ns 50 ns 10000000
StdUnorderedMap_LookupEqualRange 57 ns 57 ns 12256189
StdUnorderedMultimap_Lookup 43 ns 43 ns 16011528
StdUnorderedMultimap_LookupMissing 52 ns 51 ns 10000000
StdUnorderedMultimap_LookupEqualRange 61 ns 60 ns 11519221
```

View File

@ -0,0 +1,410 @@
#pragma once
#include <utility>
#include <functional>
#include <cstdint>
// GCC 4.9 compatibility
// GCC < 5.5 also fails to compile with `constexpr`
#if !defined(__clang__) && defined(__GNUC__) && ((__GNUC__ < 5) || (__GNUC__ == 5) && (__GNUC_MINOR__ < 5))
#define MAPBOX_ETERNAL_IS_CONSTEXPR 0
#define MAPBOX_ETERNAL_CONSTEXPR
#else
#define MAPBOX_ETERNAL_IS_CONSTEXPR 1
#define MAPBOX_ETERNAL_CONSTEXPR constexpr
#endif
namespace mapbox {
namespace eternal {
namespace impl {
template <typename T>
constexpr void swap(T& a, T& b) noexcept {
T tmp{ a };
a = b;
b = tmp;
}
template <typename Key>
class compare_key {
public:
const Key key;
constexpr compare_key(const Key& key_) noexcept : key(key_) {
}
template <typename Element>
constexpr bool operator<(const Element& rhs) const noexcept {
return key < rhs->first;
}
};
template <typename Key, typename Value>
class element {
public:
using key_type = Key;
using mapped_type = Value;
using value_type = std::pair<key_type, mapped_type>;
using compare_key_type = compare_key<key_type>;
constexpr element(const key_type& key, const mapped_type& value) noexcept : pair(key, value) {
}
constexpr bool operator<(const element& rhs) const noexcept {
return pair.first < rhs.pair.first;
}
constexpr bool operator<(const compare_key_type& rhs) const noexcept {
return pair.first < rhs.key;
}
constexpr const auto& operator*() const noexcept {
return pair;
}
constexpr const auto* operator->() const noexcept {
return &pair;
}
MAPBOX_ETERNAL_CONSTEXPR void swap(element& rhs) noexcept {
impl::swap(pair.first, rhs.pair.first);
impl::swap(pair.second, rhs.pair.second);
}
private:
value_type pair;
};
template <typename Key, typename Hasher = std::hash<Key>>
class compare_key_hash : public compare_key<Key> {
using base_type = compare_key<Key>;
public:
const std::size_t hash;
constexpr compare_key_hash(const Key& key_) noexcept : base_type(key_), hash(Hasher()(key_)) {
}
template <typename Element>
constexpr bool operator<(const Element& rhs) const noexcept {
return hash < rhs.hash || (!(rhs.hash < hash) && base_type::operator<(rhs));
}
};
template <typename Key, typename Value, typename Hasher = std::hash<Key>>
class element_hash : public element<Key, Value> {
using base_type = element<Key, Value>;
public:
using key_type = Key;
using mapped_type = Value;
using compare_key_type = compare_key_hash<key_type, Hasher>;
friend compare_key_type;
constexpr element_hash(const key_type& key, const mapped_type& value) noexcept
: base_type(key, value), hash(Hasher()(key)) {
}
template <typename T>
constexpr bool operator<(const T& rhs) const noexcept {
return hash < rhs.hash || (!(rhs.hash < hash) && base_type::operator<(rhs));
}
MAPBOX_ETERNAL_CONSTEXPR void swap(element_hash& rhs) noexcept {
impl::swap(hash, rhs.hash);
base_type::swap(rhs);
}
private:
std::size_t hash;
};
} // namespace impl
template <typename Element>
class iterator {
public:
constexpr iterator(const Element* pos_) noexcept : pos(pos_) {
}
constexpr bool operator==(const iterator& rhs) const noexcept {
return pos == rhs.pos;
}
constexpr bool operator!=(const iterator& rhs) const noexcept {
return pos != rhs.pos;
}
MAPBOX_ETERNAL_CONSTEXPR iterator& operator++() noexcept {
++pos;
return *this;
}
MAPBOX_ETERNAL_CONSTEXPR iterator& operator+=(std::size_t i) noexcept {
pos += i;
return *this;
}
constexpr iterator operator+(std::size_t i) const noexcept {
return pos + i;
}
MAPBOX_ETERNAL_CONSTEXPR iterator& operator--() noexcept {
--pos;
return *this;
}
MAPBOX_ETERNAL_CONSTEXPR iterator& operator-=(std::size_t i) noexcept {
pos -= i;
return *this;
}
constexpr std::size_t operator-(const iterator& rhs) const noexcept {
return std::size_t(pos - rhs.pos);
}
constexpr const auto& operator*() const noexcept {
return **pos;
}
constexpr const auto* operator->() const noexcept {
return &**pos;
}
private:
const Element* pos;
};
namespace impl {
template <typename Compare, typename Iterator, typename Key>
MAPBOX_ETERNAL_CONSTEXPR auto bound(Iterator left, Iterator right, const Key& key) noexcept {
std::size_t count = std::size_t(right - left);
while (count > 0) {
const std::size_t step = count / 2;
right = left + step;
if (Compare()(*right, key)) {
left = ++right;
count -= step + 1;
} else {
count = step;
}
}
return left;
}
struct less {
template <typename A, typename B>
constexpr bool operator()(const A& a, const B& b) const noexcept {
return a < b;
}
};
struct greater_equal {
template <typename A, typename B>
constexpr bool operator()(const A& a, const B& b) const noexcept {
return !(b < a);
}
};
template <typename Element, std::size_t N>
class map {
private:
static_assert(N > 0, "map is empty");
Element data_[N];
template <typename T, std::size_t... I>
MAPBOX_ETERNAL_CONSTEXPR map(const T (&data)[N], std::index_sequence<I...>) noexcept
: data_{ { data[I].first, data[I].second }... } {
static_assert(sizeof...(I) == N, "index_sequence has identical length");
// Yes, this is a bubblesort. It's usually evaluated at compile-time, it's fast for data
// that is already sorted (like static maps), it has a small code size, and it's stable.
for (auto left = data_, right = data_ + N - 1; data_ < right; right = left, left = data_) {
for (auto it = data_; it < right; ++it) {
if (it[1] < it[0]) {
it[0].swap(it[1]);
left = it;
}
}
}
}
using compare_key_type = typename Element::compare_key_type;
public:
template <typename T>
MAPBOX_ETERNAL_CONSTEXPR map(const T (&data)[N]) noexcept : map(data, std::make_index_sequence<N>()) {
}
using key_type = typename Element::key_type;
using mapped_type = typename Element::mapped_type;
using value_type = typename Element::value_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using const_reference = const value_type&;
using const_pointer = const value_type*;
using const_iterator = iterator<Element>;
MAPBOX_ETERNAL_CONSTEXPR bool unique() const noexcept {
for (auto right = data_ + N - 1, it = data_; it < right; ++it) {
if (!(it[0] < it[1])) {
return false;
}
}
return true;
}
MAPBOX_ETERNAL_CONSTEXPR const mapped_type& at(const key_type& key) const noexcept {
return find(key)->second;
}
constexpr std::size_t size() const noexcept {
return N;
}
constexpr const_iterator begin() const noexcept {
return data_;
}
constexpr const_iterator cbegin() const noexcept {
return begin();
}
constexpr const_iterator end() const noexcept {
return data_ + N;
}
constexpr const_iterator cend() const noexcept {
return end();
}
MAPBOX_ETERNAL_CONSTEXPR const_iterator lower_bound(const key_type& key) const noexcept {
return bound<less>(data_, data_ + N, compare_key_type{ key });
}
MAPBOX_ETERNAL_CONSTEXPR const_iterator upper_bound(const key_type& key) const noexcept {
return bound<greater_equal>(data_, data_ + N, compare_key_type{ key });
}
MAPBOX_ETERNAL_CONSTEXPR std::pair<const_iterator, const_iterator> equal_range(const key_type& key) const noexcept {
const compare_key_type compare_key{ key };
auto first = bound<less>(data_, data_ + N, compare_key);
return { first, bound<greater_equal>(first, data_ + N, compare_key) };
}
MAPBOX_ETERNAL_CONSTEXPR std::size_t count(const key_type& key) const noexcept {
const auto range = equal_range(key);
return range.second - range.first;
}
MAPBOX_ETERNAL_CONSTEXPR const_iterator find(const key_type& key) const noexcept {
const compare_key_type compare_key{ key };
auto it = bound<less>(data_, data_ + N, compare_key);
if (it != data_ + N && greater_equal()(*it, compare_key)) {
return it;
} else {
return end();
}
}
MAPBOX_ETERNAL_CONSTEXPR bool contains(const key_type& key) const noexcept {
return find(key) != end();
}
};
} // namespace impl
template <typename Key, typename Value, std::size_t N>
static constexpr auto map(const std::pair<const Key, const Value> (&items)[N]) noexcept {
return impl::map<impl::element<Key, Value>, N>(items);
}
template <typename Key, typename Value, std::size_t N>
static constexpr auto hash_map(const std::pair<const Key, const Value> (&items)[N]) noexcept {
return impl::map<impl::element_hash<Key, Value>, N>(items);
}
} // namespace eternal
} // namespace mapbox
// mapbox::eternal::string
namespace mapbox {
namespace eternal {
namespace impl {
// Use different constants for 32 bit vs. 64 bit size_t
constexpr std::size_t hash_offset =
std::conditional_t<sizeof(std::size_t) < 8,
std::integral_constant<uint32_t, 0x811C9DC5>,
std::integral_constant<uint64_t, 0xCBF29CE484222325>>::value;
constexpr std::size_t hash_prime =
std::conditional_t<sizeof(std::size_t) < 8,
std::integral_constant<uint32_t, 0x1000193>,
std::integral_constant<uint64_t, 0x100000001B3>>::value;
// FNV-1a hash
constexpr static std::size_t str_hash(const char* str,
const std::size_t value = hash_offset) noexcept {
return *str ? str_hash(str + 1, (value ^ static_cast<std::size_t>(*str)) * hash_prime) : value;
}
constexpr bool str_less(const char* lhs, const char* rhs) noexcept {
return *lhs && *rhs && *lhs == *rhs ? str_less(lhs + 1, rhs + 1) : *lhs < *rhs;
}
constexpr bool str_equal(const char* lhs, const char* rhs) noexcept {
return *lhs == *rhs && (*lhs == '\0' || str_equal(lhs + 1, rhs + 1));
}
} // namespace impl
class string {
private:
const char* data_;
public:
constexpr string(char const* data) noexcept : data_(data) {
}
constexpr string(const string&) noexcept = default;
constexpr string(string&&) noexcept = default;
MAPBOX_ETERNAL_CONSTEXPR string& operator=(const string&) noexcept = default;
MAPBOX_ETERNAL_CONSTEXPR string& operator=(string&&) noexcept = default;
constexpr bool operator<(const string& rhs) const noexcept {
return impl::str_less(data_, rhs.data_);
}
constexpr bool operator==(const string& rhs) const noexcept {
return impl::str_equal(data_, rhs.data_);
}
constexpr const char* data() const noexcept {
return data_;
}
constexpr const char* c_str() const noexcept {
return data_;
}
};
} // namespace eternal
} // namespace mapbox
namespace std {
template <>
struct hash<::mapbox::eternal::string> {
constexpr std::size_t operator()(const ::mapbox::eternal::string& str) const {
return ::mapbox::eternal::impl::str_hash(str.data());
}
};
} // namespace std

View File

@ -0,0 +1,29 @@
Software License Agreement (BSD License)
Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
Copyright 2011 Jose L. Blanco (joseluisblancoc@gmail.com). All rights reserved.
THE BSD LICENSE
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More