| /* |
| Open Asset Import Library (assimp) |
| ---------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, 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 FIReader.cpp |
| /// \brief Reader for Fast Infoset encoded binary XML files. |
| /// \date 2017 |
| /// \author Patrick Daehne |
| |
| #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER |
| |
| #include "FIReader.hpp" |
| #include "StringUtils.h" |
| |
| // Workaround for issue #1361 |
| // https://github.com/assimp/assimp/issues/1361 |
| #ifdef __ANDROID__ |
| # define _GLIBCXX_USE_C99 1 |
| #endif |
| |
| #include "Exceptional.h" |
| #include <assimp/IOStream.hpp> |
| #include <assimp/types.h> |
| #include "MemoryIOWrapper.h" |
| #include "irrXMLWrapper.h" |
| #include "../contrib/utf8cpp/source/utf8.h" |
| #include "fast_atof.h" |
| #include <stack> |
| #include <map> |
| #include <iostream> |
| #include <sstream> |
| #include <iomanip> |
| |
| namespace Assimp { |
| |
| static const std::string parseErrorMessage = "Fast Infoset parse error"; |
| |
| static const char *xmlDeclarations[] = { |
| "<?xml encoding='finf'?>", |
| "<?xml encoding='finf' standalone='yes'?>", |
| "<?xml encoding='finf' standalone='no'?>", |
| "<?xml version='1.0' encoding='finf'?>", |
| "<?xml version='1.0' encoding='finf' standalone='yes'?>", |
| "<?xml version='1.0' encoding='finf' standalone='no'?>", |
| "<?xml version='1.1' encoding='finf'?>", |
| "<?xml version='1.1' encoding='finf' standalone='yes'?>", |
| "<?xml version='1.1' encoding='finf' standalone='no'?>" |
| }; |
| |
| static size_t parseMagic(const uint8_t *data, const uint8_t *dataEnd) { |
| if (dataEnd - data < 4) { |
| return 0; |
| } |
| uint32_t magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; |
| switch (magic) { |
| case 0xe0000001: |
| return 4; |
| case 0x3c3f786d: // "<?xm" |
| { |
| size_t xmlDeclarationsLength = sizeof(xmlDeclarations) / sizeof(xmlDeclarations[0]); |
| for (size_t i = 0; i < xmlDeclarationsLength; ++i) { |
| auto xmlDeclaration = xmlDeclarations[i]; |
| ptrdiff_t xmlDeclarationLength = strlen(xmlDeclaration); |
| if ((dataEnd - data >= xmlDeclarationLength) && (memcmp(xmlDeclaration, data, xmlDeclarationLength) == 0)) { |
| data += xmlDeclarationLength; |
| if (dataEnd - data < 4) { |
| return 0; |
| } |
| magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; |
| return magic == 0xe0000001 ? xmlDeclarationLength + 4 : 0; |
| } |
| } |
| return 0; |
| } |
| default: |
| return 0; |
| } |
| } |
| |
| static std::string parseUTF8String(const uint8_t *data, size_t len) { |
| return std::string((char*)data, len); |
| } |
| |
| static std::string parseUTF16String(const uint8_t *data, size_t len) { |
| if (len & 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| size_t numShorts = len / 2; |
| std::vector<short> utf16; |
| utf16.reserve(numShorts); |
| for (size_t i = 0; i < numShorts; ++i) { |
| short v = (data[0] << 8) | data[1]; |
| utf16.push_back(v); |
| data += 2; |
| } |
| std::string result; |
| utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(result)); |
| return result; |
| } |
| |
| struct FIStringValueImpl: public FIStringValue { |
| inline FIStringValueImpl(std::string &&value_) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { return value; } |
| }; |
| |
| std::shared_ptr<FIStringValue> FIStringValue::create(std::string &&value) { |
| return std::make_shared<FIStringValueImpl>(std::move(value)); |
| } |
| |
| struct FIHexValueImpl: public FIHexValue { |
| mutable std::string strValue; |
| mutable bool strValueValid; |
| inline FIHexValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { |
| if (!strValueValid) { |
| strValueValid = true; |
| std::ostringstream os; |
| os << std::hex << std::uppercase << std::setfill('0'); |
| std::for_each(value.begin(), value.end(), [&](uint8_t c) { os << std::setw(2) << static_cast<int>(c); }); |
| strValue = os.str(); |
| } |
| return strValue; |
| }; |
| }; |
| |
| std::shared_ptr<FIHexValue> FIHexValue::create(std::vector<uint8_t> &&value) { |
| return std::make_shared<FIHexValueImpl>(std::move(value)); |
| } |
| |
| struct FIBase64ValueImpl: public FIBase64Value { |
| mutable std::string strValue; |
| mutable bool strValueValid; |
| inline FIBase64ValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { |
| if (!strValueValid) { |
| strValueValid = true; |
| std::ostringstream os; |
| uint8_t c1 = 0, c2; |
| int imod3 = 0; |
| std::vector<uint8_t>::size_type valueSize = value.size(); |
| for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) { |
| c2 = value[i]; |
| switch (imod3) { |
| case 0: |
| os << basis_64[c2 >> 2]; |
| imod3 = 1; |
| break; |
| case 1: |
| os << basis_64[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)]; |
| imod3 = 2; |
| break; |
| case 2: |
| os << basis_64[((c1 & 0x0f) << 2) | ((c2 & 0xc0) >> 6)] << basis_64[c2 & 0x3f]; |
| imod3 = 0; |
| break; |
| } |
| c1 = c2; |
| } |
| switch (imod3) { |
| case 1: |
| os << basis_64[(c1 & 0x03) << 4] << "=="; |
| break; |
| case 2: |
| os << basis_64[(c1 & 0x0f) << 2] << '='; |
| break; |
| } |
| strValue = os.str(); |
| } |
| return strValue; |
| }; |
| static const char basis_64[]; |
| }; |
| |
| const char FIBase64ValueImpl::basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
| |
| std::shared_ptr<FIBase64Value> FIBase64Value::create(std::vector<uint8_t> &&value) { |
| return std::make_shared<FIBase64ValueImpl>(std::move(value)); |
| } |
| |
| struct FIShortValueImpl: public FIShortValue { |
| mutable std::string strValue; |
| mutable bool strValueValid; |
| inline FIShortValueImpl(std::vector<int16_t> &&value_): strValueValid(false) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { |
| if (!strValueValid) { |
| strValueValid = true; |
| std::ostringstream os; |
| int n = 0; |
| std::for_each(value.begin(), value.end(), [&](int16_t s) { if (++n > 1) os << ' '; os << s; }); |
| strValue = os.str(); |
| } |
| return strValue; |
| } |
| }; |
| |
| std::shared_ptr<FIShortValue> FIShortValue::create(std::vector<int16_t> &&value) { |
| return std::make_shared<FIShortValueImpl>(std::move(value)); |
| } |
| |
| struct FIIntValueImpl: public FIIntValue { |
| mutable std::string strValue; |
| mutable bool strValueValid; |
| inline FIIntValueImpl(std::vector<int32_t> &&value_): strValueValid(false) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { |
| if (!strValueValid) { |
| strValueValid = true; |
| std::ostringstream os; |
| int n = 0; |
| std::for_each(value.begin(), value.end(), [&](int32_t i) { if (++n > 1) os << ' '; os << i; }); |
| strValue = os.str(); |
| } |
| return strValue; |
| }; |
| }; |
| |
| std::shared_ptr<FIIntValue> FIIntValue::create(std::vector<int32_t> &&value) { |
| return std::make_shared<FIIntValueImpl>(std::move(value)); |
| } |
| |
| struct FILongValueImpl: public FILongValue { |
| mutable std::string strValue; |
| mutable bool strValueValid; |
| inline FILongValueImpl(std::vector<int64_t> &&value_): strValueValid(false) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { |
| if (!strValueValid) { |
| strValueValid = true; |
| std::ostringstream os; |
| int n = 0; |
| std::for_each(value.begin(), value.end(), [&](int64_t l) { if (++n > 1) os << ' '; os << l; }); |
| strValue = os.str(); |
| } |
| return strValue; |
| }; |
| }; |
| |
| std::shared_ptr<FILongValue> FILongValue::create(std::vector<int64_t> &&value) { |
| return std::make_shared<FILongValueImpl>(std::move(value)); |
| } |
| |
| struct FIBoolValueImpl: public FIBoolValue { |
| mutable std::string strValue; |
| mutable bool strValueValid; |
| inline FIBoolValueImpl(std::vector<bool> &&value_): strValueValid(false) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { |
| if (!strValueValid) { |
| strValueValid = true; |
| std::ostringstream os; |
| os << std::boolalpha; |
| int n = 0; |
| std::for_each(value.begin(), value.end(), [&](bool b) { if (++n > 1) os << ' '; os << b; }); |
| strValue = os.str(); |
| } |
| return strValue; |
| }; |
| }; |
| |
| std::shared_ptr<FIBoolValue> FIBoolValue::create(std::vector<bool> &&value) { |
| return std::make_shared<FIBoolValueImpl>(std::move(value)); |
| } |
| |
| struct FIFloatValueImpl: public FIFloatValue { |
| mutable std::string strValue; |
| mutable bool strValueValid; |
| inline FIFloatValueImpl(std::vector<float> &&value_): strValueValid(false) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { |
| if (!strValueValid) { |
| strValueValid = true; |
| std::ostringstream os; |
| int n = 0; |
| std::for_each(value.begin(), value.end(), [&](float f) { if (++n > 1) os << ' '; os << f; }); |
| strValue = os.str(); |
| } |
| return strValue; |
| } |
| }; |
| |
| std::shared_ptr<FIFloatValue> FIFloatValue::create(std::vector<float> &&value) { |
| return std::make_shared<FIFloatValueImpl>(std::move(value)); |
| } |
| |
| struct FIDoubleValueImpl: public FIDoubleValue { |
| mutable std::string strValue; |
| mutable bool strValueValid; |
| inline FIDoubleValueImpl(std::vector<double> &&value_): strValueValid(false) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { |
| if (!strValueValid) { |
| strValueValid = true; |
| std::ostringstream os; |
| int n = 0; |
| std::for_each(value.begin(), value.end(), [&](double d) { if (++n > 1) os << ' '; os << d; }); |
| strValue = os.str(); |
| } |
| return strValue; |
| } |
| }; |
| |
| std::shared_ptr<FIDoubleValue> FIDoubleValue::create(std::vector<double> &&value) { |
| return std::make_shared<FIDoubleValueImpl>(std::move(value)); |
| } |
| |
| struct FIUUIDValueImpl: public FIUUIDValue { |
| mutable std::string strValue; |
| mutable bool strValueValid; |
| inline FIUUIDValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { |
| if (!strValueValid) { |
| strValueValid = true; |
| std::ostringstream os; |
| os << std::hex << std::uppercase << std::setfill('0'); |
| std::vector<uint8_t>::size_type valueSize = value.size(); |
| for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) { |
| switch (i & 15) { |
| case 0: |
| if (i > 0) { |
| os << ' '; |
| } |
| os << std::setw(2) << static_cast<int>(value[i]); |
| break; |
| case 4: |
| case 6: |
| case 8: |
| case 10: |
| os << '-'; |
| // intentionally fall through! |
| case 1: |
| case 2: |
| case 3: |
| case 5: |
| case 7: |
| case 9: |
| case 11: |
| case 12: |
| case 13: |
| case 14: |
| case 15: |
| os << std::setw(2) << static_cast<int>(value[i]); |
| break; |
| } |
| } |
| strValue = os.str(); |
| } |
| return strValue; |
| }; |
| }; |
| |
| std::shared_ptr<FIUUIDValue> FIUUIDValue::create(std::vector<uint8_t> &&value) { |
| return std::make_shared<FIUUIDValueImpl>(std::move(value)); |
| } |
| |
| struct FICDATAValueImpl: public FICDATAValue { |
| inline FICDATAValueImpl(std::string &&value_) { value = std::move(value_); } |
| virtual const std::string &toString() const /*override*/ { return value; } |
| }; |
| |
| std::shared_ptr<FICDATAValue> FICDATAValue::create(std::string &&value) { |
| return std::make_shared<FICDATAValueImpl>(std::move(value)); |
| } |
| |
| struct FIHexDecoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| return FIHexValue::create(std::vector<uint8_t>(data, data + len)); |
| } |
| }; |
| |
| struct FIBase64Decoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| return FIBase64Value::create(std::vector<uint8_t>(data, data + len)); |
| } |
| }; |
| |
| struct FIShortDecoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| if (len & 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| std::vector<int16_t> value; |
| size_t numShorts = len / 2; |
| value.reserve(numShorts); |
| for (size_t i = 0; i < numShorts; ++i) { |
| int16_t v = (data[0] << 8) | data[1]; |
| value.push_back(v); |
| data += 2; |
| } |
| return FIShortValue::create(std::move(value)); |
| } |
| }; |
| |
| struct FIIntDecoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| if (len & 3) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| std::vector<int32_t> value; |
| size_t numInts = len / 4; |
| value.reserve(numInts); |
| for (size_t i = 0; i < numInts; ++i) { |
| int32_t v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; |
| value.push_back(v); |
| data += 4; |
| } |
| return FIIntValue::create(std::move(value)); |
| } |
| }; |
| |
| struct FILongDecoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| if (len & 7) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| std::vector<int64_t> value; |
| size_t numLongs = len / 8; |
| value.reserve(numLongs); |
| for (size_t i = 0; i < numLongs; ++i) { |
| int64_t b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7]; |
| int64_t v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; |
| value.push_back(v); |
| data += 8; |
| } |
| return FILongValue::create(std::move(value)); |
| } |
| }; |
| |
| struct FIBoolDecoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| if (len < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| std::vector<bool> value; |
| uint8_t b = *data++; |
| size_t unusedBits = b >> 4; |
| size_t numBools = (len * 8) - 4 - unusedBits; |
| value.reserve(numBools); |
| uint8_t mask = 1 << 3; |
| for (size_t i = 0; i < numBools; ++i) { |
| if (!mask) { |
| mask = 1 << 7; |
| b = *data++; |
| } |
| value.push_back((b & mask) != 0); |
| } |
| return FIBoolValue::create(std::move(value)); |
| } |
| }; |
| |
| struct FIFloatDecoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| if (len & 3) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| std::vector<float> value; |
| size_t numFloats = len / 4; |
| value.reserve(numFloats); |
| for (size_t i = 0; i < numFloats; ++i) { |
| int v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; |
| float f; |
| memcpy(&f, &v, 4); |
| value.push_back(f); |
| data += 4; |
| } |
| return FIFloatValue::create(std::move(value)); |
| } |
| }; |
| |
| struct FIDoubleDecoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| if (len & 7) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| std::vector<double> value; |
| size_t numDoubles = len / 8; |
| value.reserve(numDoubles); |
| for (size_t i = 0; i < numDoubles; ++i) { |
| long long b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7]; |
| long long v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; |
| double f; |
| memcpy(&f, &v, 8); |
| value.push_back(f); |
| data += 8; |
| } |
| return FIDoubleValue::create(std::move(value)); |
| } |
| }; |
| |
| struct FIUUIDDecoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| if (len & 15) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| return FIUUIDValue::create(std::vector<uint8_t>(data, data + len)); |
| } |
| }; |
| |
| struct FICDATADecoder: public FIDecoder { |
| virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { |
| return FICDATAValue::create(parseUTF8String(data, len)); |
| } |
| }; |
| |
| class CFIReaderImpl: public FIReader { |
| public: |
| |
| CFIReaderImpl(std::unique_ptr<uint8_t[]> data_, size_t size): |
| data(std::move(data_)), dataP(data.get()), dataEnd(data.get() + size), currentNodeType(irr::io::EXN_NONE), |
| emptyElement(false), headerPending(true), terminatorPending(false) |
| {} |
| |
| virtual ~CFIReaderImpl() {} |
| |
| virtual bool read() /*override*/ { |
| if (headerPending) { |
| headerPending = false; |
| parseHeader(); |
| } |
| if (terminatorPending) { |
| terminatorPending = false; |
| if (elementStack.empty()) { |
| return false; |
| } |
| else { |
| nodeName = elementStack.top(); |
| elementStack.pop(); |
| currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END; |
| return true; |
| } |
| } |
| if (dataP >= dataEnd) { |
| return false; |
| } |
| uint8_t b = *dataP; |
| if (b < 0x80) { // Element (C.2.11.2, C.3.7.2) |
| // C.3 |
| parseElement(); |
| return true; |
| } |
| else if (b < 0xc0) { // Characters (C.3.7.5) |
| // C.7 |
| auto chars = parseNonIdentifyingStringOrIndex3(vocabulary.charactersTable); |
| nodeName = chars->toString(); |
| currentNodeType = irr::io::EXN_TEXT; |
| return true; |
| } |
| else if (b < 0xe0) { |
| if ((b & 0xfc) == 0xc4) { // DTD (C.2.11.5) |
| // C.9 |
| ++dataP; |
| if (b & 0x02) { |
| /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); |
| } |
| if (b & 0x01) { |
| /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); |
| } |
| elementStack.push(EmptyString); |
| currentNodeType = irr::io::EXN_UNKNOWN; |
| return true; |
| } |
| else if ((b & 0xfc) == 0xc8) { // Unexpanded entity reference (C.3.7.4) |
| // C.6 |
| ++dataP; |
| /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); |
| if (b & 0x02) { |
| /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); |
| } |
| if (b & 0x01) { |
| /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); |
| } |
| currentNodeType = irr::io::EXN_UNKNOWN; |
| return true; |
| } |
| } |
| else if (b < 0xf0) { |
| if (b == 0xe1) { // Processing instruction (C.2.11.3, C.3.7.3) |
| // C.5 |
| ++dataP; |
| /*const std::string &target =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| /*std::shared_ptr<const FIValue> data =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable); |
| currentNodeType = irr::io::EXN_UNKNOWN; |
| return true; |
| } |
| else if (b == 0xe2) { // Comment (C.2.11.4, C.3.7.6) |
| // C.8 |
| ++dataP; |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| std::shared_ptr<const FIValue> comment = parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable); |
| nodeName = comment->toString(); |
| currentNodeType = irr::io::EXN_COMMENT; |
| return true; |
| } |
| } |
| else { // Terminator (C.2.12, C.3.8) |
| ++dataP; |
| if (b == 0xff) { |
| terminatorPending = true; |
| } |
| if (elementStack.empty()) { |
| return false; |
| } |
| else { |
| nodeName = elementStack.top(); |
| elementStack.pop(); |
| currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END; |
| return true; |
| } |
| } |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| |
| virtual irr::io::EXML_NODE getNodeType() const /*override*/ { |
| return currentNodeType; |
| } |
| |
| virtual int getAttributeCount() const /*override*/ { |
| return static_cast<int>(attributes.size()); |
| } |
| |
| virtual const char* getAttributeName(int idx) const /*override*/ { |
| if (idx < 0 || idx >= (int)attributes.size()) { |
| return nullptr; |
| } |
| return attributes[idx].name.c_str(); |
| } |
| |
| virtual const char* getAttributeValue(int idx) const /*override*/ { |
| if (idx < 0 || idx >= (int)attributes.size()) { |
| return nullptr; |
| } |
| return attributes[idx].value->toString().c_str(); |
| } |
| |
| virtual const char* getAttributeValue(const char* name) const /*override*/ { |
| const Attribute* attr = getAttributeByName(name); |
| if (!attr) { |
| return nullptr; |
| } |
| return attr->value->toString().c_str(); |
| } |
| |
| virtual const char* getAttributeValueSafe(const char* name) const /*override*/ { |
| const Attribute* attr = getAttributeByName(name); |
| if (!attr) { |
| return EmptyString.c_str(); |
| } |
| return attr->value->toString().c_str(); |
| } |
| |
| virtual int getAttributeValueAsInt(const char* name) const /*override*/ { |
| const Attribute* attr = getAttributeByName(name); |
| if (!attr) { |
| return 0; |
| } |
| std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attr->value); |
| if (intValue) { |
| return intValue->value.size() == 1 ? intValue->value.front() : 0; |
| } |
| return atoi(attr->value->toString().c_str()); |
| } |
| |
| virtual int getAttributeValueAsInt(int idx) const /*override*/ { |
| if (idx < 0 || idx >= (int)attributes.size()) { |
| return 0; |
| } |
| std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attributes[idx].value); |
| if (intValue) { |
| return intValue->value.size() == 1 ? intValue->value.front() : 0; |
| } |
| return atoi(attributes[idx].value->toString().c_str()); |
| } |
| |
| virtual float getAttributeValueAsFloat(const char* name) const /*override*/ { |
| const Attribute* attr = getAttributeByName(name); |
| if (!attr) { |
| return 0; |
| } |
| std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attr->value); |
| if (floatValue) { |
| return floatValue->value.size() == 1 ? floatValue->value.front() : 0; |
| } |
| |
| return fast_atof(attr->value->toString().c_str()); |
| } |
| |
| virtual float getAttributeValueAsFloat(int idx) const /*override*/ { |
| if (idx < 0 || idx >= (int)attributes.size()) { |
| return 0; |
| } |
| std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attributes[idx].value); |
| if (floatValue) { |
| return floatValue->value.size() == 1 ? floatValue->value.front() : 0; |
| } |
| return fast_atof(attributes[idx].value->toString().c_str()); |
| } |
| |
| virtual const char* getNodeName() const /*override*/ { |
| return nodeName.c_str(); |
| } |
| |
| virtual const char* getNodeData() const /*override*/ { |
| return nodeName.c_str(); |
| } |
| |
| virtual bool isEmptyElement() const /*override*/ { |
| return emptyElement; |
| } |
| |
| virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ { |
| return irr::io::ETF_UTF8; |
| } |
| |
| virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ { |
| return irr::io::ETF_UTF8; |
| } |
| |
| virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int idx) const /*override*/ { |
| if (idx < 0 || idx >= (int)attributes.size()) { |
| return nullptr; |
| } |
| return attributes[idx].value; |
| } |
| |
| virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* name) const /*override*/ { |
| const Attribute* attr = getAttributeByName(name); |
| if (!attr) { |
| return nullptr; |
| } |
| return attr->value; |
| } |
| |
| virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr<FIDecoder> decoder) /*override*/ { |
| decoderMap[algorithmUri] = std::move(decoder); |
| } |
| |
| virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *vocabulary) /*override*/ { |
| vocabularyMap[vocabularyUri] = vocabulary; |
| } |
| |
| private: |
| |
| struct QName { |
| std::string prefix; |
| std::string uri; |
| std::string name; |
| inline QName() {} |
| inline QName(const FIQName &qname): prefix(qname.prefix ? qname.prefix : ""), uri(qname.uri ? qname.uri : ""), name(qname.name) {} |
| }; |
| |
| struct Attribute { |
| QName qname; |
| std::string name; |
| std::shared_ptr<const FIValue> value; |
| }; |
| |
| struct Vocabulary { |
| std::vector<std::string> restrictedAlphabetTable; |
| std::vector<std::string> encodingAlgorithmTable; |
| std::vector<std::string> prefixTable; |
| std::vector<std::string> namespaceNameTable; |
| std::vector<std::string> localNameTable; |
| std::vector<std::string> otherNCNameTable; |
| std::vector<std::string> otherURITable; |
| std::vector<std::shared_ptr<const FIValue>> attributeValueTable; |
| std::vector<std::shared_ptr<const FIValue>> charactersTable; |
| std::vector<std::shared_ptr<const FIValue>> otherStringTable; |
| std::vector<QName> elementNameTable; |
| std::vector<QName> attributeNameTable; |
| Vocabulary() { |
| prefixTable.push_back("xml"); |
| namespaceNameTable.push_back("http://www.w3.org/XML/1998/namespace"); |
| } |
| }; |
| |
| const Attribute* getAttributeByName(const char* name) const { |
| if (!name) { |
| return 0; |
| } |
| std::string n = name; |
| for (int i=0; i<(int)attributes.size(); ++i) { |
| if (attributes[i].name == n) { |
| return &attributes[i]; |
| } |
| } |
| return 0; |
| } |
| |
| size_t parseInt2() { // C.25 |
| uint8_t b = *dataP++; |
| if (!(b & 0x40)) { // x0...... (C.25.2) |
| return b & 0x3f; |
| } |
| else if ((b & 0x60) == 0x40) { // x10..... ........ (C.25.3) |
| if (dataEnd - dataP > 0) { |
| return (((b & 0x1f) << 8) | *dataP++) + 0x40; |
| } |
| } |
| else if ((b & 0x70) == 0x60) { // x110.... ........ ........ (C.25.4) |
| if (dataEnd - dataP > 1) { |
| size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x2040; |
| dataP += 2; |
| return result; |
| } |
| } |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| |
| size_t parseInt3() { // C.27 |
| uint8_t b = *dataP++; |
| if (!(b & 0x20)) { // xx0..... (C.27.2) |
| return b & 0x1f; |
| } |
| else if ((b & 0x38) == 0x20) { // xx100... ........ (C.27.3) |
| if (dataEnd - dataP > 0) { |
| return (((b & 0x07) << 8) | *dataP++) + 0x20; |
| } |
| } |
| else if ((b & 0x38) == 0x28) { // xx101... ........ ........ (C.27.4) |
| if (dataEnd - dataP > 1) { |
| size_t result = (((b & 0x07) << 16) | (dataP[0] << 8) | dataP[1]) + 0x820; |
| dataP += 2; |
| return result; |
| } |
| } |
| else if ((b & 0x3f) == 0x30) { // xx110000 0000.... ........ ........ (C.27.5) |
| if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) { |
| size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x80820; |
| dataP += 3; |
| return result; |
| } |
| } |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| |
| size_t parseInt4() { // C.28 |
| uint8_t b = *dataP++; |
| if (!(b & 0x10)) { // xxx0.... (C.28.2) |
| return b & 0x0f; |
| } |
| else if ((b & 0x1c) == 0x10) { // xxx100.. ........ (C.28.3) |
| if (dataEnd - dataP > 0) { |
| return (((b & 0x03) << 8) | *dataP++) + 0x10; |
| } |
| } |
| else if ((b & 0x1c) == 0x14) { // xxx101.. ........ ........ (C.28.4) |
| if (dataEnd - dataP > 1) { |
| size_t result = (((b & 0x03) << 16) | (dataP[0] << 8) | dataP[1]) + 0x410; |
| dataP += 2; |
| return result; |
| } |
| } |
| else if ((b & 0x1f) == 0x18) { // xxx11000 0000.... ........ ........ (C.28.5) |
| if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) { |
| size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x40410; |
| dataP += 3; |
| return result; |
| } |
| } |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| |
| size_t parseSequenceLen() { // C.21 |
| if (dataEnd - dataP > 0) { |
| uint8_t b = *dataP++; |
| if (b < 0x80) { // 0....... (C.21.2) |
| return b; |
| } |
| else if ((b & 0xf0) == 0x80) { // 1000.... ........ ........ (C.21.3) |
| if (dataEnd - dataP > 1) { |
| size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x80; |
| dataP += 2; |
| return result; |
| } |
| } |
| } |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| |
| std::string parseNonEmptyOctetString2() { // C.22 |
| // Parse the length of the string |
| uint8_t b = *dataP++ & 0x7f; |
| size_t len; |
| if (!(b & 0x40)) { // x0...... (C.22.3.1) |
| len = b + 1; |
| } |
| else if (b == 0x40) { // x1000000 ........ (C.22.3.2) |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| len = *dataP++ + 0x41; |
| } |
| else if (b == 0x60) { // x1100000 ........ ........ ........ ........ (C.22.3.3) |
| if (dataEnd - dataP < 4) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| len = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x141; |
| dataP += 4; |
| } |
| else { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| |
| // Parse the string (C.22.4) |
| if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| std::string s = parseUTF8String(dataP, len); |
| dataP += len; |
| |
| return s; |
| } |
| |
| size_t parseNonEmptyOctetString5Length() { // C.23 |
| // Parse the length of the string |
| size_t b = *dataP++ & 0x0f; |
| if (!(b & 0x08)) { // xxxx0... (C.23.3.1) |
| return b + 1; |
| } |
| else if (b == 0x08) { // xxxx1000 ........ (C.23.3.2) |
| if (dataEnd - dataP > 0) { |
| return *dataP++ + 0x09; |
| } |
| } |
| else if (b == 0x0c) { // xxxx1100 ........ ........ ........ ........ (C.23.3.3) |
| if (dataEnd - dataP > 3) { |
| size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x109; |
| dataP += 4; |
| return result; |
| } |
| } |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| |
| size_t parseNonEmptyOctetString7Length() { // C.24 |
| // Parse the length of the string |
| size_t b = *dataP++ & 0x03; |
| if (!(b & 0x02)) { // xxxxxx0. (C.24.3.1) |
| return b + 1; |
| } |
| else if (b == 0x02) { // xxxxxx10 ........ (C.24.3.2) |
| if (dataEnd - dataP > 0) { |
| return *dataP++ + 0x3; |
| } |
| } |
| else if (b == 0x03) { // xxxxxx11 ........ ........ ........ ........ (C.24.3.3) |
| if (dataEnd - dataP > 3) { |
| size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x103; |
| dataP += 4; |
| return result; |
| } |
| } |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| |
| std::shared_ptr<const FIValue> parseEncodedData(size_t index, size_t len) { |
| if (index < 32) { |
| FIDecoder *decoder = defaultDecoder[index]; |
| if (!decoder) { |
| throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index)); |
| } |
| return decoder->decode(dataP, len); |
| } |
| else { |
| if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) { |
| throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index)); |
| } |
| std::string uri = vocabulary.encodingAlgorithmTable[index - 32]; |
| auto it = decoderMap.find(uri); |
| if (it == decoderMap.end()) { |
| throw DeadlyImportError("Unsupported encoding algorithm " + uri); |
| } |
| else { |
| return it->second->decode(dataP, len); |
| } |
| } |
| } |
| |
| std::shared_ptr<const FIValue> parseRestrictedAlphabet(size_t index, size_t len) { |
| std::string alphabet; |
| if (index < 16) { |
| switch (index) { |
| case 0: // numeric |
| alphabet = "0123456789-+.e "; |
| break; |
| case 1: // date and time |
| alphabet = "0123456789-:TZ "; |
| break; |
| default: |
| throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index)); |
| } |
| } |
| else { |
| if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) { |
| throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index)); |
| } |
| alphabet = vocabulary.restrictedAlphabetTable[index - 16]; |
| } |
| std::vector<uint32_t> alphabetUTF32; |
| utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32)); |
| std::string::size_type alphabetLength = alphabetUTF32.size(); |
| if (alphabetLength < 2) { |
| throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength)); |
| } |
| std::string::size_type bitsPerCharacter = 1; |
| while ((1ull << bitsPerCharacter) <= alphabetLength) { |
| ++bitsPerCharacter; |
| } |
| size_t bitsAvail = 0; |
| uint8_t mask = (1 << bitsPerCharacter) - 1; |
| uint32_t bits = 0; |
| std::string s; |
| for (size_t i = 0; i < len; ++i) { |
| bits = (bits << 8) | dataP[i]; |
| bitsAvail += 8; |
| while (bitsAvail >= bitsPerCharacter) { |
| bitsAvail -= bitsPerCharacter; |
| size_t charIndex = (bits >> bitsAvail) & mask; |
| if (charIndex < alphabetLength) { |
| s.push_back(alphabetUTF32[charIndex]); |
| } |
| else if (charIndex != mask) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| } |
| } |
| return FIStringValue::create(std::move(s)); |
| } |
| |
| std::shared_ptr<const FIValue> parseEncodedCharacterString3() { // C.19 |
| std::shared_ptr<const FIValue> result; |
| size_t len; |
| uint8_t b = *dataP; |
| if (b & 0x20) { |
| ++dataP; |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| size_t index = ((b & 0x0f) << 4) | ((*dataP & 0xf0) >> 4); // C.29 |
| len = parseNonEmptyOctetString5Length(); |
| if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| if (b & 0x10) { |
| // encoding algorithm (C.19.3.4) |
| result = parseEncodedData(index, len); |
| } |
| else { |
| // Restricted alphabet (C.19.3.3) |
| result = parseRestrictedAlphabet(index, len); |
| } |
| } |
| else { |
| len = parseNonEmptyOctetString5Length(); |
| if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| if (b & 0x10) { |
| // UTF-16 (C.19.3.2) |
| if (len & 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| result = FIStringValue::create(parseUTF16String(dataP, len)); |
| } |
| else { |
| // UTF-8 (C.19.3.1) |
| result = FIStringValue::create(parseUTF8String(dataP, len)); |
| } |
| } |
| dataP += len; |
| return result; |
| } |
| |
| std::shared_ptr<const FIValue> parseEncodedCharacterString5() { // C.20 |
| std::shared_ptr<const FIValue> result; |
| size_t len; |
| uint8_t b = *dataP; |
| if (b & 0x08) { |
| ++dataP; |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| size_t index = ((b & 0x03) << 6) | ((*dataP & 0xfc) >> 2); /* C.29 */ |
| len = parseNonEmptyOctetString7Length(); |
| if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| if (b & 0x04) { |
| // encoding algorithm (C.20.3.4) |
| result = parseEncodedData(index, len); |
| } |
| else { |
| // Restricted alphabet (C.20.3.3) |
| result = parseRestrictedAlphabet(index, len); |
| } |
| } |
| else { |
| len = parseNonEmptyOctetString7Length(); |
| if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| if (b & 0x04) { |
| // UTF-16 (C.20.3.2) |
| if (len & 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| result = FIStringValue::create(parseUTF16String(dataP, len)); |
| } |
| else { |
| // UTF-8 (C.20.3.1) |
| result = FIStringValue::create(parseUTF8String(dataP, len)); |
| } |
| } |
| dataP += len; |
| return result; |
| } |
| |
| const std::string &parseIdentifyingStringOrIndex(std::vector<std::string> &stringTable) { // C.13 |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| uint8_t b = *dataP; |
| if (b & 0x80) { |
| // We have an index (C.13.4) |
| size_t index = parseInt2(); |
| if (index >= stringTable.size()) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| return stringTable[index]; |
| } |
| else { |
| // We have a string (C.13.3) |
| stringTable.push_back(parseNonEmptyOctetString2()); |
| return stringTable.back(); |
| } |
| } |
| |
| QName parseNameSurrogate() { // C.16 |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| uint8_t b = *dataP++; |
| if (b & 0xfc) { // Padding '000000' C.2.5.5 |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| QName result; |
| size_t index; |
| if (b & 0x02) { // prefix (C.16.3) |
| if ((dataEnd - dataP < 1) || (*dataP & 0x80)) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| index = parseInt2(); |
| if (index >= vocabulary.prefixTable.size()) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| result.prefix = vocabulary.prefixTable[index]; |
| } |
| if (b & 0x01) { // namespace-name (C.16.4) |
| if ((dataEnd - dataP < 1) || (*dataP & 0x80)) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| index = parseInt2(); |
| if (index >= vocabulary.namespaceNameTable.size()) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| result.uri = vocabulary.namespaceNameTable[index]; |
| } |
| // local-name |
| if ((dataEnd - dataP < 1) || (*dataP & 0x80)) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| index = parseInt2(); |
| if (index >= vocabulary.localNameTable.size()) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| result.name = vocabulary.localNameTable[index]; |
| return result; |
| } |
| |
| const QName &parseQualifiedNameOrIndex2(std::vector<QName> &qNameTable) { // C.17 |
| uint8_t b = *dataP; |
| if ((b & 0x7c) == 0x78) { // x11110.. |
| // We have a literal (C.17.3) |
| ++dataP; |
| QName result; |
| // prefix (C.17.3.1) |
| result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string(); |
| // namespace-name (C.17.3.1) |
| result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string(); |
| // local-name |
| result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable); |
| qNameTable.push_back(result); |
| return qNameTable.back(); |
| } |
| else { |
| // We have an index (C.17.4) |
| size_t index = parseInt2(); |
| if (index >= qNameTable.size()) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| return qNameTable[index]; |
| } |
| } |
| |
| const QName &parseQualifiedNameOrIndex3(std::vector<QName> &qNameTable) { // C.18 |
| uint8_t b = *dataP; |
| if ((b & 0x3c) == 0x3c) { // xx1111.. |
| // We have a literal (C.18.3) |
| ++dataP; |
| QName result; |
| // prefix (C.18.3.1) |
| result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string(); |
| // namespace-name (C.18.3.1) |
| result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string(); |
| // local-name |
| result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable); |
| qNameTable.push_back(result); |
| return qNameTable.back(); |
| } |
| else { |
| // We have an index (C.18.4) |
| size_t index = parseInt3(); |
| if (index >= qNameTable.size()) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| return qNameTable[index]; |
| } |
| } |
| |
| std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex1(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.14 |
| uint8_t b = *dataP; |
| if (b == 0xff) { // C.26.2 |
| // empty string |
| ++dataP; |
| return EmptyFIString; |
| } |
| else if (b & 0x80) { // C.14.4 |
| // We have an index |
| size_t index = parseInt2(); |
| if (index >= valueTable.size()) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| return valueTable[index]; |
| } |
| else { // C.14.3 |
| // We have a literal |
| std::shared_ptr<const FIValue> result = parseEncodedCharacterString3(); |
| if (b & 0x40) { // C.14.3.1 |
| valueTable.push_back(result); |
| } |
| return result; |
| } |
| } |
| |
| std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex3(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.15 |
| uint8_t b = *dataP; |
| if (b & 0x20) { // C.15.4 |
| // We have an index |
| size_t index = parseInt4(); |
| if (index >= valueTable.size()) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| return valueTable[index]; |
| } |
| else { // C.15.3 |
| // We have a literal |
| std::shared_ptr<const FIValue> result = parseEncodedCharacterString5(); |
| if (b & 0x10) { // C.15.3.1 |
| valueTable.push_back(result); |
| } |
| return result; |
| } |
| } |
| |
| void parseElement() { |
| // C.3 |
| |
| attributes.clear(); |
| |
| uint8_t b = *dataP; |
| bool hasAttributes = (b & 0x40) != 0; // C.3.3 |
| if ((b & 0x3f) == 0x38) { // C.3.4.1 |
| // Parse namespaces |
| ++dataP; |
| for (;;) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| b = *dataP++; |
| if (b == 0xf0) { // C.3.4.3 |
| break; |
| } |
| if ((b & 0xfc) != 0xcc) { // C.3.4.2 |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| // C.12 |
| Attribute attr; |
| attr.qname.prefix = "xmlns"; |
| attr.qname.name = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string(); |
| attr.qname.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string(); |
| attr.name = attr.qname.name.empty() ? "xmlns" : "xmlns:" + attr.qname.name; |
| attr.value = FIStringValue::create(std::string(attr.qname.uri)); |
| attributes.push_back(attr); |
| } |
| if ((dataEnd - dataP < 1) || (*dataP & 0xc0)) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| } |
| |
| // Parse Element name (C.3.5) |
| const QName &elemName = parseQualifiedNameOrIndex3(vocabulary.elementNameTable); |
| nodeName = elemName.prefix.empty() ? elemName.name : elemName.prefix + ':' + elemName.name; |
| |
| if (hasAttributes) { |
| for (;;) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| b = *dataP; |
| if (b < 0x80) { // C.3.6.1 |
| // C.4 |
| Attribute attr; |
| attr.qname = parseQualifiedNameOrIndex2(vocabulary.attributeNameTable); |
| attr.name = attr.qname.prefix.empty() ? attr.qname.name : attr.qname.prefix + ':' + attr.qname.name; |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| attr.value = parseNonIdentifyingStringOrIndex1(vocabulary.attributeValueTable); |
| attributes.push_back(attr); |
| } |
| else { |
| if ((b & 0xf0) != 0xf0) { // C.3.6.2 |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| emptyElement = b == 0xff; // C.3.6.2, C.3.8 |
| ++dataP; |
| break; |
| } |
| } |
| } |
| else { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| b = *dataP; |
| switch (b) { |
| case 0xff: |
| terminatorPending = true; |
| // Intentionally fall through |
| case 0xf0: |
| emptyElement = true; |
| ++dataP; |
| break; |
| default: |
| emptyElement = false; |
| } |
| } |
| if (!emptyElement) { |
| elementStack.push(nodeName); |
| } |
| |
| currentNodeType = irr::io::EXN_ELEMENT; |
| } |
| |
| void parseHeader() { |
| // Parse header (C.1.3) |
| size_t magicSize = parseMagic(dataP, dataEnd); |
| if (!magicSize) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| dataP += magicSize; |
| // C.2.3 |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| uint8_t b = *dataP++; |
| if (b & 0x40) { |
| // Parse additional data (C.2.4) |
| size_t len = parseSequenceLen(); |
| for (size_t i = 0; i < len; ++i) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| /*std::string id =*/ parseNonEmptyOctetString2(); |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| /*std::string data =*/ parseNonEmptyOctetString2(); |
| } |
| } |
| if (b & 0x20) { |
| // Parse initial vocabulary (C.2.5) |
| if (dataEnd - dataP < 2) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| uint16_t b1 = (dataP[0] << 8) | dataP[1]; |
| dataP += 2; |
| if (b1 & 0x1000) { |
| // External vocabulary (C.2.5.2) |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| std::string uri = parseNonEmptyOctetString2(); |
| auto it = vocabularyMap.find(uri); |
| if (it == vocabularyMap.end()) { |
| throw DeadlyImportError("Unknown vocabulary " + uri); |
| } |
| const FIVocabulary *externalVocabulary = it->second; |
| if (externalVocabulary->restrictedAlphabetTable) { |
| std::copy(externalVocabulary->restrictedAlphabetTable, externalVocabulary->restrictedAlphabetTable + externalVocabulary->restrictedAlphabetTableSize, std::back_inserter(vocabulary.restrictedAlphabetTable)); |
| } |
| if (externalVocabulary->encodingAlgorithmTable) { |
| std::copy(externalVocabulary->encodingAlgorithmTable, externalVocabulary->encodingAlgorithmTable + externalVocabulary->encodingAlgorithmTableSize, std::back_inserter(vocabulary.encodingAlgorithmTable)); |
| } |
| if (externalVocabulary->prefixTable) { |
| std::copy(externalVocabulary->prefixTable, externalVocabulary->prefixTable + externalVocabulary->prefixTableSize, std::back_inserter(vocabulary.prefixTable)); |
| } |
| if (externalVocabulary->namespaceNameTable) { |
| std::copy(externalVocabulary->namespaceNameTable, externalVocabulary->namespaceNameTable + externalVocabulary->namespaceNameTableSize, std::back_inserter(vocabulary.namespaceNameTable)); |
| } |
| if (externalVocabulary->localNameTable) { |
| std::copy(externalVocabulary->localNameTable, externalVocabulary->localNameTable + externalVocabulary->localNameTableSize, std::back_inserter(vocabulary.localNameTable)); |
| } |
| if (externalVocabulary->otherNCNameTable) { |
| std::copy(externalVocabulary->otherNCNameTable, externalVocabulary->otherNCNameTable + externalVocabulary->otherNCNameTableSize, std::back_inserter(vocabulary.otherNCNameTable)); |
| } |
| if (externalVocabulary->otherURITable) { |
| std::copy(externalVocabulary->otherURITable, externalVocabulary->otherURITable + externalVocabulary->otherURITableSize, std::back_inserter(vocabulary.otherURITable)); |
| } |
| if (externalVocabulary->attributeValueTable) { |
| std::copy(externalVocabulary->attributeValueTable, externalVocabulary->attributeValueTable + externalVocabulary->attributeValueTableSize, std::back_inserter(vocabulary.attributeValueTable)); |
| } |
| if (externalVocabulary->charactersTable) { |
| std::copy(externalVocabulary->charactersTable, externalVocabulary->charactersTable + externalVocabulary->charactersTableSize, std::back_inserter(vocabulary.charactersTable)); |
| } |
| if (externalVocabulary->otherStringTable) { |
| std::copy(externalVocabulary->otherStringTable, externalVocabulary->otherStringTable + externalVocabulary->otherStringTableSize, std::back_inserter(vocabulary.otherStringTable)); |
| } |
| if (externalVocabulary->elementNameTable) { |
| std::copy(externalVocabulary->elementNameTable, externalVocabulary->elementNameTable + externalVocabulary->elementNameTableSize, std::back_inserter(vocabulary.elementNameTable)); |
| } |
| if (externalVocabulary->attributeNameTable) { |
| std::copy(externalVocabulary->attributeNameTable, externalVocabulary->attributeNameTable + externalVocabulary->attributeNameTableSize, std::back_inserter(vocabulary.attributeNameTable)); |
| } |
| } |
| if (b1 & 0x0800) { |
| // Parse restricted alphabets (C.2.5.3) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.restrictedAlphabetTable.push_back(parseNonEmptyOctetString2()); |
| } |
| } |
| if (b1 & 0x0400) { |
| // Parse encoding algorithms (C.2.5.3) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.encodingAlgorithmTable.push_back(parseNonEmptyOctetString2()); |
| } |
| } |
| if (b1 & 0x0200) { |
| // Parse prefixes (C.2.5.3) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.prefixTable.push_back(parseNonEmptyOctetString2()); |
| } |
| } |
| if (b1 & 0x0100) { |
| // Parse namespace names (C.2.5.3) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.namespaceNameTable.push_back(parseNonEmptyOctetString2()); |
| } |
| } |
| if (b1 & 0x0080) { |
| // Parse local names (C.2.5.3) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.localNameTable.push_back(parseNonEmptyOctetString2()); |
| } |
| } |
| if (b1 & 0x0040) { |
| // Parse other ncnames (C.2.5.3) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.otherNCNameTable.push_back(parseNonEmptyOctetString2()); |
| } |
| } |
| if (b1 & 0x0020) { |
| // Parse other uris (C.2.5.3) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.otherURITable.push_back(parseNonEmptyOctetString2()); |
| } |
| } |
| if (b1 & 0x0010) { |
| // Parse attribute values (C.2.5.4) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.attributeValueTable.push_back(parseEncodedCharacterString3()); |
| } |
| } |
| if (b1 & 0x0008) { |
| // Parse content character chunks (C.2.5.4) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.charactersTable.push_back(parseEncodedCharacterString3()); |
| } |
| } |
| if (b1 & 0x0004) { |
| // Parse other strings (C.2.5.4) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| vocabulary.otherStringTable.push_back(parseEncodedCharacterString3()); |
| } |
| } |
| if (b1 & 0x0002) { |
| // Parse element name surrogates (C.2.5.5) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| vocabulary.elementNameTable.push_back(parseNameSurrogate()); |
| } |
| } |
| if (b1 & 0x0001) { |
| // Parse attribute name surrogates (C.2.5.5) |
| for (size_t len = parseSequenceLen(); len > 0; --len) { |
| vocabulary.attributeNameTable.push_back(parseNameSurrogate()); |
| } |
| } |
| } |
| if (b & 0x10) { |
| // Parse notations (C.2.6) |
| for (;;) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| uint8_t b1 = *dataP++; |
| if (b1 == 0xf0) { |
| break; |
| } |
| if ((b1 & 0xfc) != 0xc0) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| /* C.11 */ |
| /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); |
| if (b1 & 0x02) { |
| /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); |
| } |
| if (b1 & 0x01) { |
| /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); |
| } |
| } |
| } |
| if (b & 0x08) { |
| // Parse unparsed entities (C.2.7) |
| for (;;) { |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| uint8_t b1 = *dataP++; |
| if (b1 == 0xf0) { |
| break; |
| } |
| if ((b1 & 0xfe) != 0xd0) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| /* C.10 */ |
| /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); |
| /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); |
| if (b1 & 0x01) { |
| /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); |
| } |
| /*const std::string ¬ationName =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); |
| } |
| } |
| if (b & 0x04) { |
| // Parse character encoding scheme (C.2.8) |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| /*std::string characterEncodingScheme =*/ parseNonEmptyOctetString2(); |
| } |
| if (b & 0x02) { |
| // Parse standalone flag (C.2.9) |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| uint8_t b1 = *dataP++; |
| if (b1 & 0xfe) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| //bool standalone = b1 & 0x01; |
| } |
| if (b & 0x01) { |
| // Parse version (C.2.10) |
| if (dataEnd - dataP < 1) { |
| throw DeadlyImportError(parseErrorMessage); |
| } |
| /*std::shared_ptr<const FIValue> version =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable); |
| } |
| } |
| |
| std::unique_ptr<uint8_t[]> data; |
| uint8_t *dataP, *dataEnd; |
| irr::io::EXML_NODE currentNodeType; |
| bool emptyElement; |
| bool headerPending; |
| bool terminatorPending; |
| Vocabulary vocabulary; |
| std::vector<Attribute> attributes; |
| std::stack<std::string> elementStack; |
| std::string nodeName; |
| std::map<std::string, std::unique_ptr<FIDecoder>> decoderMap; |
| std::map<std::string, const FIVocabulary*> vocabularyMap; |
| |
| static const std::string EmptyString; |
| static std::shared_ptr<const FIValue> EmptyFIString; |
| |
| static FIHexDecoder hexDecoder; |
| static FIBase64Decoder base64Decoder; |
| static FIShortDecoder shortDecoder; |
| static FIIntDecoder intDecoder; |
| static FILongDecoder longDecoder; |
| static FIBoolDecoder boolDecoder; |
| static FIFloatDecoder floatDecoder; |
| static FIDoubleDecoder doubleDecoder; |
| static FIUUIDDecoder uuidDecoder; |
| static FICDATADecoder cdataDecoder; |
| static FIDecoder *defaultDecoder[32]; |
| }; |
| |
| const std::string CFIReaderImpl::EmptyString; |
| std::shared_ptr<const FIValue> CFIReaderImpl::EmptyFIString = FIStringValue::create(std::string()); |
| |
| FIHexDecoder CFIReaderImpl::hexDecoder; |
| FIBase64Decoder CFIReaderImpl::base64Decoder; |
| FIShortDecoder CFIReaderImpl::shortDecoder; |
| FIIntDecoder CFIReaderImpl::intDecoder; |
| FILongDecoder CFIReaderImpl::longDecoder; |
| FIBoolDecoder CFIReaderImpl::boolDecoder; |
| FIFloatDecoder CFIReaderImpl::floatDecoder; |
| FIDoubleDecoder CFIReaderImpl::doubleDecoder; |
| FIUUIDDecoder CFIReaderImpl::uuidDecoder; |
| FICDATADecoder CFIReaderImpl::cdataDecoder; |
| |
| FIDecoder *CFIReaderImpl::defaultDecoder[32] = { |
| &hexDecoder, |
| &base64Decoder, |
| &shortDecoder, |
| &intDecoder, |
| &longDecoder, |
| &boolDecoder, |
| &floatDecoder, |
| &doubleDecoder, |
| &uuidDecoder, |
| &cdataDecoder |
| }; |
| |
| class CXMLReaderImpl : public FIReader |
| { |
| public: |
| |
| //! Constructor |
| CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader_) |
| : reader(std::move(reader_)) |
| {} |
| |
| virtual ~CXMLReaderImpl() {} |
| |
| virtual bool read() /*override*/ { |
| return reader->read(); |
| } |
| |
| virtual irr::io::EXML_NODE getNodeType() const /*override*/ { |
| return reader->getNodeType(); |
| } |
| |
| virtual int getAttributeCount() const /*override*/ { |
| return reader->getAttributeCount(); |
| } |
| |
| virtual const char* getAttributeName(int idx) const /*override*/ { |
| return reader->getAttributeName(idx); |
| } |
| |
| virtual const char* getAttributeValue(int idx) const /*override*/ { |
| return reader->getAttributeValue(idx); |
| } |
| |
| virtual const char* getAttributeValue(const char* name) const /*override*/ { |
| return reader->getAttributeValue(name); |
| } |
| |
| virtual const char* getAttributeValueSafe(const char* name) const /*override*/ { |
| return reader->getAttributeValueSafe(name); |
| } |
| |
| virtual int getAttributeValueAsInt(const char* name) const /*override*/ { |
| return reader->getAttributeValueAsInt(name); |
| } |
| |
| virtual int getAttributeValueAsInt(int idx) const /*override*/ { |
| return reader->getAttributeValueAsInt(idx); |
| } |
| |
| virtual float getAttributeValueAsFloat(const char* name) const /*override*/ { |
| return reader->getAttributeValueAsFloat(name); |
| } |
| |
| virtual float getAttributeValueAsFloat(int idx) const /*override*/ { |
| return reader->getAttributeValueAsFloat(idx); |
| } |
| |
| virtual const char* getNodeName() const /*override*/ { |
| return reader->getNodeName(); |
| } |
| |
| virtual const char* getNodeData() const /*override*/ { |
| return reader->getNodeData(); |
| } |
| |
| virtual bool isEmptyElement() const /*override*/ { |
| return reader->isEmptyElement(); |
| } |
| |
| virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ { |
| return reader->getSourceFormat(); |
| } |
| |
| virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ { |
| return reader->getParserFormat(); |
| } |
| |
| virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int /*idx*/) const /*override*/ { |
| return nullptr; |
| } |
| |
| virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* /*name*/) const /*override*/ { |
| return nullptr; |
| } |
| |
| virtual void registerDecoder(const std::string & /*algorithmUri*/, std::unique_ptr<FIDecoder> /*decoder*/) /*override*/ {} |
| |
| |
| virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {} |
| |
| private: |
| |
| std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader; |
| }; |
| |
| static std::unique_ptr<uint8_t[]> readFile(IOStream *stream, size_t &size, bool &isFI) { |
| size = stream->FileSize(); |
| std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[size]); |
| if (stream->Read(data.get(), size, 1) != 1) { |
| size = 0; |
| data.reset(); |
| } |
| isFI = parseMagic(data.get(), data.get() + size) > 0; |
| return data; |
| } |
| |
| std::unique_ptr<FIReader> FIReader::create(IOStream *stream) |
| { |
| size_t size; |
| bool isFI; |
| auto data = readFile(stream, size, isFI); |
| if (isFI) { |
| return std::unique_ptr<FIReader>(new CFIReaderImpl(std::move(data), size)); |
| } |
| else { |
| auto memios = std::unique_ptr<MemoryIOStream>(new MemoryIOStream(data.release(), size, true)); |
| auto callback = std::unique_ptr<CIrrXML_IOStreamReader>(new CIrrXML_IOStreamReader(memios.get())); |
| return std::unique_ptr<FIReader>(new CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>>(createIrrXMLReader(callback.get())))); |
| } |
| } |
| |
| }// namespace Assimp |
| |
| #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER |