// slang-json-value.cpp #include "slang-json-value.h" #include "../core/slang-string-escape-util.h" #include "../core/slang-string-util.h" namespace Slang { /* static */ const JSONValue::Kind JSONValue::g_typeToKind[] = { JSONValue::Kind::Invalid, // Invalid JSONValue::Kind::Bool, // True, JSONValue::Kind::Bool, // False JSONValue::Kind::Null, // Null, JSONValue::Kind::String, // StringLexeme, JSONValue::Kind::Integer, // IntegerLexeme, JSONValue::Kind::Float, // FloatLexeme, JSONValue::Kind::Integer, // IntegerValue, JSONValue::Kind::Float, // FloatValue, JSONValue::Kind::String, // StringValue, JSONValue::Kind::String, // StringRepresentation JSONValue::Kind::Array, // Array, JSONValue::Kind::Object, // Object, }; static bool _isDefault(const RttiInfo* type, const void* in) { SLANG_UNUSED(type) const JSONValue& value = *(const JSONValue*)in; return value.getKind() == JSONValue::Kind::Invalid; } static OtherRttiInfo _getJSONValueRttiInfo() { OtherRttiInfo info; info.init(RttiInfo::Kind::Other); info.m_name = "JSONValue"; info.m_isDefaultFunc = _isDefault; info.m_typeFuncs = GetRttiTypeFuncs::getFuncs(); return info; } /* static */ const OtherRttiInfo JSONValue::g_rttiInfo = _getJSONValueRttiInfo(); static JSONKeyValue _makeInvalidKeyValue() { JSONKeyValue keyValue; keyValue.key = JSONKey(0); keyValue.value.type = JSONValue::Type::Invalid; return keyValue; } /* static */ JSONKeyValue g_invalid = _makeInvalidKeyValue(); /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! JSONValue !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ bool JSONValue::asBool() const { switch (type) { case JSONValue::Type::True: return true; case JSONValue::Type::False: case JSONValue::Type::Null: { return false; } case JSONValue::Type::IntegerValue: return intValue != 0; case JSONValue::Type::FloatValue: return floatValue != 0; default: break; } if (isLexeme(type)) { SLANG_ASSERT(!"Lexeme values can only be accessed through container"); } else { SLANG_ASSERT(!"Not bool convertable"); } return false; } int64_t JSONValue::asInteger() const { switch (type) { case JSONValue::Type::True: return 1; case JSONValue::Type::False: case JSONValue::Type::Null: { return 0; } case JSONValue::Type::IntegerValue: return intValue; case JSONValue::Type::FloatValue: return int64_t(floatValue); break; } if (isLexeme(type)) { SLANG_ASSERT(!"Lexeme values can only be accessed through container"); } else { SLANG_ASSERT(!"Not int convertable"); } return 0; } double JSONValue::asFloat() const { switch (type) { case JSONValue::Type::True: return 1.0; case JSONValue::Type::False: case JSONValue::Type::Null: { return 0.0; } case JSONValue::Type::IntegerValue: return double(intValue); case JSONValue::Type::FloatValue: return floatValue; default: break; } if (isLexeme(type)) { SLANG_ASSERT(!"Lexeme values can only be accessed through container"); } else { SLANG_ASSERT(!"Not float convertable"); } return 0; } /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! PersistentJSONValue !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ PersistentJSONValue::PersistentJSONValue(const ThisType& rhs) { *(JSONValue*)this = rhs; if (type == Type::StringRepresentation && stringRep) { stringRep->addReference(); } } void PersistentJSONValue::operator=(const ThisType& rhs) { if (this != &rhs) { if (rhs.type == Type::StringRepresentation && rhs.stringRep) { rhs.stringRep->addReference(); } if (type == Type::StringRepresentation && stringRep) { stringRep->releaseReference(); } *(JSONValue*)this = rhs; } } String PersistentJSONValue::getString() const { if (type == Type::StringRepresentation) { return String(stringRep); } SLANG_ASSERT(!"Not a string type"); return String(); } UnownedStringSlice PersistentJSONValue::getSlice() const { if (type == Type::StringRepresentation) { return StringRepresentation::asSlice(stringRep); } SLANG_ASSERT(!"Not a string type"); return UnownedStringSlice(); } void PersistentJSONValue::set(const UnownedStringSlice& slice, SourceLoc inLoc) { StringRepresentation* oldRep = (type == JSONValue::Type::StringRepresentation) ? stringRep : nullptr; type = Type::StringRepresentation; loc = inLoc; StringRepresentation* newRep = nullptr; const auto sliceLength = slice.getLength(); // If we have an oldRep that is unique and large enough reuse it if (sliceLength) { if (oldRep && oldRep->isUniquelyReferenced() && sliceLength <= oldRep->capacity) { oldRep->setContents(slice); newRep = oldRep; // We are reusing so make null so not freed oldRep = nullptr; } else { newRep = StringRepresentation::createWithReference(slice); } SLANG_ASSERT(newRep->debugGetReferenceCount() >= 1); } stringRep = newRep; if (oldRep) { oldRep->releaseReference(); } } void PersistentJSONValue::_init(const UnownedStringSlice& slice, SourceLoc inLoc) { loc = inLoc; type = Type::StringRepresentation; stringRep = StringRepresentation::createWithReference(slice); } bool PersistentJSONValue::operator==(const ThisType& rhs) const { if (this == &rhs) { return true; } if (type != rhs.type || loc != rhs.loc) { return false; } switch (type) { case Type::Invalid: case Type::True: case Type::False: case Type::Null: { // The type is all that needs to be checked return true; } case Type::IntegerValue: return intValue == rhs.intValue; case Type::FloatValue: return floatValue == rhs.floatValue; case Type::StringRepresentation: { if (stringRep == rhs.stringRep) { return true; } auto thisSlice = StringRepresentation::asSlice(stringRep); auto rhsSlice = StringRepresentation::asSlice(rhs.stringRep); return thisSlice == rhsSlice; } default: break; } SLANG_ASSERT(!"Not valid Persistent type"); return false; } void PersistentJSONValue::_init(const JSONValue& in, JSONContainer* container) { // We are assuming this is invalid, so it can't be the same as in SLANG_ASSERT(&in != this); switch (in.type) { case Type::StringValue: case Type::StringLexeme: { if (!container) { SLANG_ASSERT(!"Requires container"); return; } _init(container->getTransientString(in), in.loc); break; } case Type::StringRepresentation: { *(JSONValue*)this = in; if (stringRep) { stringRep->addReference(); } break; } case Type::IntegerLexeme: { type = JSONValue::Type::IntegerValue; intValue = container->asInteger(in); loc = in.loc; break; } case Type::FloatLexeme: { type = JSONValue::Type::FloatValue; floatValue = container->asFloat(in); loc = in.loc; break; } case Type::Array: case Type::Object: { SLANG_ASSERT(!"Not a simple JSON type"); break; } default: { *(JSONValue*)this = in; break; } } } void PersistentJSONValue::set(const JSONValue& in, JSONContainer* container) { if (&in != this) { if (type == Type::StringRepresentation) { StringRepresentation* oldStringRep = stringRep; _init(in, container); if (oldStringRep) { oldStringRep->releaseReference(); } } else { _init(in, container); } } } /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! JSONContainer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ JSONContainer::JSONContainer(SourceManager* sourceManager) : m_slicePool(StringSlicePool::Style::Default), m_sourceManager(sourceManager) { // Index 0 is the empty array or object _addRange(Range::Type::None, 0, 0); } void JSONContainer::reset() { m_slicePool.clear(); m_freeRangeIndices.clear(); m_arrayValues.clear(); m_objectValues.clear(); _addRange(Range::Type::None, 0, 0); m_currentView = nullptr; } /* static */ bool JSONContainer::areKeysUnique(const JSONKeyValue* keyValues, Index keyValueCount) { for (Index i = 1; i < keyValueCount; ++i) { const JSONKey key = keyValues[i].key; for (Int j = 0; j < i - 1; j++) { if (keyValues[j].key == key) { return false; } } } return true; } Index JSONContainer::_addRange(Range::Type type, Index startIndex, Index count) { if (m_freeRangeIndices.getCount() > 0) { const Index rangeIndex = m_freeRangeIndices.getLast(); m_freeRangeIndices.removeLast(); auto& range = m_ranges[rangeIndex]; range.type = type; range.startIndex = startIndex; range.count = count; range.capacity = count; return rangeIndex; } else { Range range; range.type = type; range.startIndex = startIndex; range.count = count; range.capacity = count; m_ranges.add(range); return m_ranges.getCount() - 1; } } JSONValue JSONContainer::createArray(const JSONValue* values, Index valuesCount, SourceLoc loc) { if (valuesCount <= 0) { return JSONValue::makeEmptyArray(loc); } JSONValue value; value.type = JSONValue::Type::Array; value.loc = loc; value.rangeIndex = _addRange(Range::Type::Array, m_arrayValues.getCount(), valuesCount); m_arrayValues.addRange(values, valuesCount); return value; } JSONValue JSONContainer::createObject( const JSONKeyValue* keyValues, Index keyValueCount, SourceLoc loc) { if (keyValueCount <= 0) { return JSONValue::makeEmptyObject(loc); } JSONValue value; value.type = JSONValue::Type::Object; value.loc = loc; value.rangeIndex = _addRange(Range::Type::Object, m_objectValues.getCount(), keyValueCount); m_objectValues.addRange(keyValues, keyValueCount); return value; } JSONValue JSONContainer::createString(const UnownedStringSlice& slice, SourceLoc loc) { JSONValue value; value.type = JSONValue::Type::StringValue; value.loc = loc; value.stringKey = getKey(slice); return value; } JSONKey JSONContainer::getKey(const UnownedStringSlice& slice) { return JSONKey(m_slicePool.add(slice)); } JSONKey JSONContainer::findKey(const UnownedStringSlice& slice) const { const Index index = m_slicePool.findIndex(slice); return (index < 0) ? JSONKey(0) : JSONKey(index); } ConstArrayView JSONContainer::getArray(const JSONValue& in) const { SLANG_ASSERT(in.type == JSONValue::Type::Array); if (in.type != JSONValue::Type::Array || in.rangeIndex == 0) { return ConstArrayView((const JSONValue*)nullptr, 0); } const Range& range = m_ranges[in.rangeIndex]; return ConstArrayView(m_arrayValues.getBuffer() + range.startIndex, range.count); } ConstArrayView JSONContainer::getObject(const JSONValue& in) const { SLANG_ASSERT(in.type == JSONValue::Type::Object); if (in.type != JSONValue::Type::Object || in.rangeIndex == 0) { return ConstArrayView((const JSONKeyValue*)nullptr, 0); } const Range& range = m_ranges[in.rangeIndex]; return ConstArrayView(m_objectValues.getBuffer() + range.startIndex, range.count); } ArrayView JSONContainer::getArray(const JSONValue& in) { SLANG_ASSERT(in.type == JSONValue::Type::Array); if (in.type != JSONValue::Type::Array || in.rangeIndex == 0) { return ArrayView((JSONValue*)nullptr, 0); } const Range& range = m_ranges[in.rangeIndex]; SLANG_ASSERT( range.startIndex <= m_arrayValues.getCount() && range.startIndex + range.count <= m_arrayValues.getCount()); return ArrayView(m_arrayValues.getBuffer() + range.startIndex, range.count); } ArrayView JSONContainer::getObject(const JSONValue& in) { SLANG_ASSERT(in.type == JSONValue::Type::Object); if (in.type != JSONValue::Type::Object || in.rangeIndex == 0) { return ArrayView((JSONKeyValue*)nullptr, 0); } const Range& range = m_ranges[in.rangeIndex]; return ArrayView(m_objectValues.getBuffer() + range.startIndex, range.count); } UnownedStringSlice JSONContainer::getLexeme(const JSONValue& in) { SLANG_ASSERT(JSONValue::isLexeme(in.type)); if (!JSONValue::isLexeme(in.type)) { return UnownedStringSlice(); } if (!(m_currentView && m_currentView->getRange().contains(in.loc))) { m_currentView = m_sourceManager->findSourceView(in.loc); if (!m_currentView) { return UnownedStringSlice(); } } const auto offset = m_currentView->getRange().getOffset(in.loc); SourceFile* sourceFile = m_currentView->getSourceFile(); return UnownedStringSlice(sourceFile->getContent().begin() + offset, in.length); } UnownedStringSlice JSONContainer::getString(const JSONValue& in) { switch (in.type) { case JSONValue::Type::StringValue: { return getStringFromKey(in.stringKey); } case JSONValue::Type::StringLexeme: { auto slice = getTransientString(in); auto handle = m_slicePool.add(slice); return m_slicePool.getSlice(handle); } case JSONValue::Type::StringRepresentation: { return StringRepresentation::asSlice(in.stringRep); } case JSONValue::Type::Null: { return UnownedStringSlice(); } default: break; } SLANG_ASSERT(!"Not a string type"); return UnownedStringSlice(); } UnownedStringSlice JSONContainer::getTransientString(const JSONValue& in) { switch (in.type) { case JSONValue::Type::StringRepresentation: { return StringRepresentation::asSlice(in.stringRep); } case JSONValue::Type::StringValue: { return getStringFromKey(in.stringKey); } case JSONValue::Type::StringLexeme: { StringEscapeHandler* handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::JSON); UnownedStringSlice lexeme = getLexeme(in); UnownedStringSlice unquoted = StringEscapeUtil::unquote(handler, lexeme); if (handler->isUnescapingNeeeded(unquoted)) { m_buf.clear(); handler->appendUnescaped(unquoted, m_buf); return m_buf.getUnownedSlice(); } else { return unquoted; } } case JSONValue::Type::Null: { return UnownedStringSlice(); } } SLANG_ASSERT(!"Not a string type"); return UnownedStringSlice(); } JSONKey JSONContainer::getStringKey(const JSONValue& in) { return (in.type == JSONValue::Type::StringValue) ? in.stringKey : getKey(getTransientString(in)); } bool JSONContainer::asBool(const JSONValue& value) { switch (value.type) { case JSONValue::Type::IntegerLexeme: return asInteger(value) != 0; case JSONValue::Type::FloatLexeme: return asFloat(value) != 0.0; default: return value.asBool(); } } JSONValue JSONContainer::asValue(const JSONValue& inValue) { JSONValue value = inValue; switch (value.type) { case JSONValue::Type::StringLexeme: { const UnownedStringSlice slice = getTransientString(inValue); value.stringKey = getKey(slice); value.type = JSONValue::Type::StringValue; break; } case JSONValue::Type::IntegerLexeme: { value.floatValue = value.asFloat(); value.type = JSONValue::Type::IntegerValue; break; } case JSONValue::Type::FloatLexeme: { value.floatValue = value.asFloat(); value.type = JSONValue::Type::FloatValue; break; } default: break; } return value; } void JSONContainer::_clearSourceManagerDependency(JSONValue* ioValues, Index count) { for (Index i = 0; i < count; ++i) { auto& value = ioValues[i]; value = asValue(value); value.loc = SourceLoc(); } } void JSONContainer::clearSourceManagerDependency(JSONValue* ioValues, Index valuesCount) { _clearSourceManagerDependency(ioValues, valuesCount); // We need to find ranges that are available for (auto& range : m_ranges) { switch (range.type) { case Range::Type::Array: { _clearSourceManagerDependency( m_arrayValues.getBuffer() + range.startIndex, range.count); break; } case Range::Type::Object: { const Index count = range.count; auto pairs = m_objectValues.getBuffer() + range.startIndex; for (Index i = 0; i < count; ++i) { auto& pair = pairs[i]; pair.keyLoc = SourceLoc(); pair.value = asValue(pair.value); pair.value.loc = SourceLoc(); } break; } default: break; } } // Remove the source manager m_sourceManager = nullptr; } int64_t JSONContainer::asInteger(const JSONValue& value) { switch (value.type) { case JSONValue::Type::IntegerLexeme: { UnownedStringSlice slice = getLexeme(value); int64_t intValue; if (SLANG_SUCCEEDED(StringUtil::parseInt64(slice, intValue))) { return intValue; } SLANG_ASSERT(!"Couldn't convert int"); return 0; } case JSONValue::Type::FloatLexeme: return int64_t(asFloat(value)); default: return value.asInteger(); } } double JSONContainer::asFloat(const JSONValue& value) { switch (value.type) { case JSONValue::Type::IntegerLexeme: return double(asInteger(value)); case JSONValue::Type::FloatLexeme: { UnownedStringSlice slice = getLexeme(value); double floatValue; if (SLANG_SUCCEEDED(StringUtil::parseDouble(slice, floatValue))) { return floatValue; } SLANG_ASSERT(!"Couldn't convert double"); return 0.0; } default: return value.asFloat(); } } Index JSONContainer::findObjectIndex(const JSONValue& obj, JSONKey key) const { auto pairs = getObject(obj); return pairs.findFirstIndex( [key](const JSONKeyValue& pair) -> bool { return pair.key == key; }); } JSONValue JSONContainer::findObjectValue(const JSONValue& obj, JSONKey key) const { auto pairs = getObject(obj); const Index index = pairs.findFirstIndex([key](const JSONKeyValue& pair) -> bool { return pair.key == key; }); return (index >= 0) ? pairs[index].value : JSONValue::makeInvalid(); } JSONValue& JSONContainer::getAt(const JSONValue& array, Index index) { SLANG_ASSERT(array.type == JSONValue::Type::Array); const Range& range = m_ranges[array.rangeIndex]; SLANG_ASSERT(index >= 0 && index < range.count); return m_arrayValues[range.startIndex + index]; } void JSONContainer::addToArray(JSONValue& array, const JSONValue& value) { SLANG_ASSERT(array.type == JSONValue::Type::Array); if (array.type == JSONValue::Type::Array) { // If it's empty if (array.rangeIndex == 0) { // We can just add to the end array.rangeIndex = _addRange(Range::Type::Array, m_arrayValues.getCount(), 1); m_arrayValues.add(value); } else { _add(m_ranges[array.rangeIndex], m_arrayValues, value); } } } Index JSONContainer::findKeyGlobalIndex(const JSONValue& obj, JSONKey key) { SLANG_ASSERT(obj.type == JSONValue::Type::Object); if (obj.type != JSONValue::Type::Object) { return -1; } auto buf = m_objectValues.getBuffer(); const Range& range = m_ranges[obj.rangeIndex]; for (Index i = range.startIndex; i < range.startIndex + range.count; ++i) { if (buf[i].key == key) { return i; } } return -1; } Index JSONContainer::findKeyGlobalIndex(const JSONValue& obj, const UnownedStringSlice& slice) { Index keyIndex = m_slicePool.findIndex(slice); if (keyIndex < 0) { return -1; } return findKeyGlobalIndex(obj, JSONKey(keyIndex)); } void JSONContainer::_removeKey(JSONValue& obj, Index globalIndex) { Range& range = m_ranges[obj.rangeIndex]; const auto localIndex = globalIndex + range.startIndex; if (localIndex < range.count - 1) { auto localBuf = m_objectValues.getBuffer() + range.startIndex; ::memmove( (void*)(localBuf + localIndex), (void*)(localBuf + localIndex + 1), sizeof(*localBuf) * (range.count - (localIndex + 1))); } --range.count; } bool JSONContainer::removeKey(JSONValue& obj, JSONKey key) { const Index globalIndex = findKeyGlobalIndex(obj, key); if (globalIndex >= 0) { _removeKey(obj, globalIndex); return true; } return false; } bool JSONContainer::removeKey(JSONValue& obj, const UnownedStringSlice& slice) { const Index globalIndex = findKeyGlobalIndex(obj, slice); if (globalIndex >= 0) { _removeKey(obj, globalIndex); return true; } return false; } template /* static */ void JSONContainer::_add(Range& ioRange, List& ioList, const T& value) { // If we have capacity, we can add to the end if (ioRange.count < ioRange.capacity) { ioList[ioRange.startIndex + ioRange.count++] = value; return; } // If we are at the end, we can just add if (ioRange.startIndex + ioRange.capacity == ioList.getCount()) { ioList.add(value); ioRange.capacity++; ioRange.count++; return; } // Okay we have no choice but to make new space at the end // So there's no place to add. We want to move to the end with an extra space. const Index newStartIndex = ioList.getCount(); ioList.growToCount(newStartIndex + ioRange.count + 1); auto buffer = ioList.getBuffer(); ::memmove( (void*)(buffer + newStartIndex), (void*)(buffer + ioRange.startIndex), sizeof(*buffer) * ioRange.count); buffer[newStartIndex + ioRange.count] = value; ioRange.startIndex = newStartIndex; ioRange.count++; ioRange.capacity++; } void JSONContainer::setKeyValue(JSONValue& obj, JSONKey key, const JSONValue& value, SourceLoc loc) { SLANG_ASSERT(obj.type == JSONValue::Type::Object); if (obj.type != JSONValue::Type::Object) { return; } const JSONKeyValue keyValue{key, loc, value}; if (obj.rangeIndex == 0) { // We need a new range and add to the end obj.rangeIndex = _addRange(Range::Type::Object, m_objectValues.getCount(), 1); m_objectValues.add(keyValue); return; } const Index globalIndex = findKeyGlobalIndex(obj, key); if (globalIndex >= 0) { auto& dst = m_objectValues[globalIndex]; SLANG_ASSERT(dst.key == key); dst = keyValue; return; } Range& range = m_ranges[obj.rangeIndex]; _add(range, m_objectValues, keyValue); } void JSONContainer::_destroyRange(Index rangeIndex) { auto& range = m_ranges[rangeIndex]; // If the range is at the end, shrink it switch (range.type) { case Range::Type::Array: { if (range.startIndex + range.capacity == m_arrayValues.getCount()) { m_arrayValues.setCount(range.startIndex); } break; } case Range::Type::Object: { if (range.startIndex + range.capacity == m_objectValues.getCount()) { m_objectValues.setCount(range.startIndex); } break; } default: break; } range.type = Range::Type::Destroyed; m_freeRangeIndices.add(rangeIndex); } void JSONContainer::destroy(JSONValue& value) { if (value.needsDestroy()) { _destroyRange(value.rangeIndex); } value.type = JSONValue::Type::Invalid; } void JSONContainer::destroyRecursively(JSONValue& inValue) { if (!(inValue.needsDestroy() && m_ranges[inValue.rangeIndex].isActive())) { inValue.type = JSONValue::Type::Invalid; return; } inValue.type = JSONValue::Type::Invalid; List activeRanges; activeRanges.add(m_ranges[inValue.rangeIndex]); _destroyRange(inValue.rangeIndex); while (activeRanges.getCount()) { const Range range = activeRanges.getLast(); activeRanges.removeLast(); auto type = range.type; const Index count = range.count; if (type == Range::Type::Array) { auto* buf = m_arrayValues.getBuffer() + range.startIndex; for (Index i = 0; i < count; ++i) { auto& value = buf[i]; // If we have an active range, add to work list, and destroy if (value.needsDestroy() && m_ranges[value.rangeIndex].isActive()) { activeRanges.add(m_ranges[value.rangeIndex]); _destroyRange(value.rangeIndex); } value.type = JSONValue::Type::Invalid; } } else { SLANG_ASSERT(type == Range::Type::Object); auto* buf = m_objectValues.getBuffer() + range.startIndex; for (Index i = 0; i < count; ++i) { auto& keyValue = buf[i]; auto& value = keyValue.value; // We want to mark that it's in the list so that if we have a badly formed tree we // don't read if (value.needsDestroy() && m_ranges[value.rangeIndex].isActive()) { activeRanges.add(m_ranges[value.rangeIndex]); _destroyRange(value.rangeIndex); } value.type = JSONValue::Type::Invalid; } } } } bool JSONContainer::areEqual(const JSONValue* a, const JSONValue* b, Index count) { for (Index i = 0; i < count; ++i) { if (!areEqual(a[i], b[i])) { return false; } } return true; } /* static */ bool JSONContainer::_sameKeyOrder( const JSONKeyValue* a, const JSONKeyValue* b, Index count) { for (Index i = 0; i < count; ++i) { if (a[i].key != b[i].key) { return false; } } return true; } bool JSONContainer::_areEqualOrderedKeys(const JSONKeyValue* a, const JSONKeyValue* b, Index count) { for (Index i = 0; i < count; ++i) { const auto& curA = a[i]; const auto& curB = b[i]; if (curA.key != curB.key || !areEqual(curA.value, curB.value)) { return false; } } return true; } bool JSONContainer::_areEqualValues(const JSONKeyValue* a, const JSONKeyValue* b, Index count) { for (Index i = 0; i < count; ++i) { if (!areEqual(a[i].value, b[i].value)) { return false; } } return true; } bool JSONContainer::areEqual(const JSONKeyValue* a, const JSONKeyValue* b, Index count) { if (count == 0) { return true; } if (count == 1) { return _areEqualOrderedKeys(a, b, count); } else if (_sameKeyOrder(a, b, count)) { return _areEqualValues(a, b, count); } else { // We need to compare with keys in the same order List sortedAs; sortedAs.addRange(a, count); List sortedBs; sortedBs.addRange(b, count); sortedAs.sort( [](const JSONKeyValue& a, const JSONKeyValue& b) -> bool { return a.key < b.key; }); sortedBs.sort( [](const JSONKeyValue& a, const JSONKeyValue& b) -> bool { return a.key < b.key; }); return _areEqualOrderedKeys(sortedAs.getBuffer(), sortedBs.getBuffer(), count); } } bool JSONContainer::areEqual(const JSONValue& a, const UnownedStringSlice& slice) { return a.getKind() == JSONValue::Kind::String && getTransientString(a) == slice; } bool JSONContainer::areEqual(const JSONValue& a, const JSONValue& b) { if (&a == &b) { return true; } if (a.type == b.type) { switch (a.type) { default: // Invalid are never equal case JSONValue::Type::Invalid: return false; case JSONValue::Type::True: case JSONValue::Type::False: case JSONValue::Type::Null: { return true; } case JSONValue::Type::IntegerLexeme: return asInteger(a) == asInteger(b); case JSONValue::Type::FloatLexeme: return asFloat(a) == asFloat(b); case JSONValue::Type::StringLexeme: { // If the lexemes are equal they are equal UnownedStringSlice lexemeA = getLexeme(a); UnownedStringSlice lexemeB = getLexeme(b); // Else we want to decode the string to be sure if they are equal. return lexemeA == lexemeB || getStringKey(a) == getStringKey(b); } case JSONValue::Type::IntegerValue: return a.intValue == b.intValue; case JSONValue::Type::FloatValue: return a.floatValue == b.floatValue; case JSONValue::Type::StringValue: return a.stringKey == b.stringKey; case JSONValue::Type::StringRepresentation: { return a.stringRep == b.stringRep || StringRepresentation::asSlice(a.stringRep) == StringRepresentation::asSlice(b.stringRep); } case JSONValue::Type::Array: { if (a.rangeIndex == b.rangeIndex) { return true; } auto arrayA = getArray(a); auto arrayB = getArray(b); const Index count = arrayA.getCount(); return (count == arrayB.getCount()) && areEqual(arrayA.getBuffer(), arrayB.getBuffer(), count); } case JSONValue::Type::Object: { if (a.rangeIndex == b.rangeIndex) { return true; } const auto aValues = getObject(a); const auto bValues = getObject(b); const Index count = aValues.getCount(); return (count == bValues.getCount()) && areEqual(aValues.getBuffer(), bValues.getBuffer(), count); } } } // If they are the same kind, and float/int/string we can convert to compare const JSONValue::Kind kind = a.getKind(); if (kind == b.getKind()) { switch (kind) { case JSONValue::Kind::String: return getStringKey(a) == getStringKey(b); case JSONValue::Kind::Integer: return asInteger(a) == asInteger(b); case JSONValue::Kind::Float: return asFloat(a) == asFloat(b); default: break; } } return false; } void JSONContainer::traverseRecursively(const JSONValue& value, JSONListener* listener) { typedef JSONValue::Type Type; switch (value.type) { case Type::True: return listener->addBoolValue(true, value.loc); case Type::False: return listener->addBoolValue(false, value.loc); case Type::Null: return listener->addNullValue(value.loc); case Type::StringLexeme: return listener->addLexemeValue(JSONTokenType::StringLiteral, getLexeme(value), value.loc); case Type::IntegerLexeme: return listener->addLexemeValue(JSONTokenType::IntegerLiteral, getLexeme(value), value.loc); case Type::FloatLexeme: return listener->addLexemeValue(JSONTokenType::FloatLiteral, getLexeme(value), value.loc); case Type::IntegerValue: return listener->addIntegerValue(value.intValue, value.loc); case Type::FloatValue: return listener->addFloatValue(value.floatValue, value.loc); case Type::StringValue: { const auto slice = getStringFromKey(value.stringKey); return listener->addStringValue(slice, value.loc); } case Type::StringRepresentation: { return listener->addStringValue(getTransientString(value), value.loc); } case Type::Array: { listener->startArray(value.loc); const auto arr = getArray(value); for (const auto& arrayValue : arr) { traverseRecursively(arrayValue, listener); } listener->endArray(SourceLoc()); break; } case Type::Object: { listener->startObject(value.loc); const auto obj = getObject(value); for (const auto& objKeyValue : obj) { // Emit the key const auto keyString = getStringFromKey(objKeyValue.key); listener->addUnquotedKey(keyString, objKeyValue.keyLoc); // Emit the value associated with the key traverseRecursively(objKeyValue.value, listener); } listener->endObject(SourceLoc()); break; } default: { SLANG_ASSERT(!"Invalid type"); return; } } } /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! JSONBuilder !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ JSONBuilder::JSONBuilder(JSONContainer* container, Flags flags) : m_container(container), m_flags(flags) { m_state.m_kind = State::Kind::Root; m_state.m_startIndex = 0; m_state.resetKey(); m_rootValue.reset(); } void JSONBuilder::reset() { // Reset the state m_state.m_kind = State::Kind::Root; m_state.m_startIndex = 0; m_state.resetKey(); // Clear the work values m_rootValue.reset(); // Clear the lists m_stateStack.clear(); m_values.clear(); m_keyValues.clear(); } void JSONBuilder::_popState() { SLANG_ASSERT(m_stateStack.getCount() > 0); // Reset the end depending on typpe switch (m_state.m_kind) { case State::Kind::Array: { m_values.setCount(m_state.m_startIndex); break; } case State::Kind::Object: { m_keyValues.setCount(m_state.m_startIndex); break; } } // Pop from the stack m_state = m_stateStack.getLast(); m_stateStack.removeLast(); } Index JSONBuilder::_findKeyIndex(JSONKey key) const { SLANG_ASSERT(m_state.m_kind == State::Kind::Object); const Index count = m_keyValues.getCount(); for (Index i = m_state.m_startIndex; i < count; ++i) { auto& keyValue = m_keyValues[i]; // If we find the key return it's index if (keyValue.key == key) { return i; } } return -1; } void JSONBuilder::_add(const JSONValue& value) { SLANG_ASSERT(value.isValid()); switch (m_state.m_kind) { case State::Kind::Root: { SLANG_ASSERT(!m_rootValue.isValid()); m_rootValue = value; break; } case State::Kind::Array: { m_values.add(value); break; } case State::Kind::Object: { SLANG_ASSERT(m_state.hasKey()); JSONKeyValue keyValue; keyValue.key = m_state.m_key; keyValue.keyLoc = m_state.m_keyLoc; keyValue.value = value; const Index index = _findKeyIndex(keyValue.key); if (index >= 0) { m_keyValues[index] = keyValue; } else { m_keyValues.add(keyValue); } m_state.resetKey(); break; } } } void JSONBuilder::startObject(SourceLoc loc) { m_stateStack.add(m_state); m_state.m_kind = State::Kind::Object; m_state.m_startIndex = m_keyValues.getCount(); m_state.m_loc = loc; m_state.resetKey(); } void JSONBuilder::endObject(SourceLoc loc) { SLANG_UNUSED(loc); SLANG_ASSERT(m_state.m_kind == State::Kind::Object); const Index count = m_keyValues.getCount() - m_state.m_startIndex; const JSONValue value = m_container->createObject( m_keyValues.getBuffer() + m_state.m_startIndex, count, m_state.m_loc); // Pop current state _popState(); // Add the value to the current state _add(value); } void JSONBuilder::startArray(SourceLoc loc) { m_stateStack.add(m_state); m_state.m_kind = State::Kind::Array; m_state.m_startIndex = m_values.getCount(); m_state.m_loc = loc; m_state.resetKey(); } void JSONBuilder::endArray(SourceLoc loc) { SLANG_UNUSED(loc); SLANG_ASSERT(m_state.m_kind == State::Kind::Array); const Index count = m_values.getCount() - m_state.m_startIndex; const JSONValue value = m_container->createArray(m_values.getBuffer() + m_state.m_startIndex, count, m_state.m_loc); // Pop current state _popState(); // Add the value to the current state _add(value); } void JSONBuilder::addQuotedKey(const UnownedStringSlice& key, SourceLoc loc) { // We need to decode m_work.clear(); StringEscapeHandler* handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::JSON); StringEscapeUtil::appendUnquoted(handler, key, m_work); addUnquotedKey(m_work.getUnownedSlice(), loc); } void JSONBuilder::addUnquotedKey(const UnownedStringSlice& key, SourceLoc loc) { SLANG_ASSERT(!m_state.hasKey()); m_state.setKey(m_container->getKey(key), loc); } void JSONBuilder::addLexemeValue(JSONTokenType type, const UnownedStringSlice& value, SourceLoc loc) { switch (type) { case JSONTokenType::True: return _add(JSONValue::makeBool(true, loc)); case JSONTokenType::False: return _add(JSONValue::makeBool(false, loc)); case JSONTokenType::Null: return _add(JSONValue::makeNull(loc)); case JSONTokenType::IntegerLiteral: { if (m_flags & Flag::ConvertLexemes) { int64_t intValue = -1; auto res = StringUtil::parseInt64(value, intValue); SLANG_UNUSED(res); SLANG_ASSERT(SLANG_SUCCEEDED(res)); _add(JSONValue::makeInt(intValue, loc)); } else { SLANG_ASSERT(loc.isValid()); _add(JSONValue::makeLexeme(JSONValue::Type::IntegerLexeme, loc, value.getLength())); } break; } case JSONTokenType::FloatLiteral: { if (m_flags & Flag::ConvertLexemes) { double floatValue = 0; auto res = StringUtil::parseDouble(value, floatValue); SLANG_UNUSED(res); SLANG_ASSERT(SLANG_SUCCEEDED(res)); _add(JSONValue::makeFloat(floatValue, loc)); } else { SLANG_ASSERT(loc.isValid()); _add(JSONValue::makeLexeme(JSONValue::Type::FloatLexeme, loc, value.getLength())); } break; } case JSONTokenType::StringLiteral: { if (m_flags & Flag::ConvertLexemes) { auto handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::JSON); StringBuilder buf; StringEscapeUtil::appendUnquoted(handler, value, buf); _add(m_container->createString(buf.getUnownedSlice(), loc)); } else { SLANG_ASSERT(loc.isValid()); _add(JSONValue::makeLexeme(JSONValue::Type::StringLexeme, loc, value.getLength())); } break; } default: { SLANG_ASSERT(!"Unhandled type"); } } } void JSONBuilder::addIntegerValue(int64_t value, SourceLoc loc) { _add(JSONValue::makeInt(value, loc)); } void JSONBuilder::addFloatValue(double value, SourceLoc loc) { _add(JSONValue::makeFloat(value, loc)); } void JSONBuilder::addBoolValue(bool value, SourceLoc loc) { _add(JSONValue::makeBool(value, loc)); } void JSONBuilder::addStringValue(const UnownedStringSlice& slice, SourceLoc loc) { _add(m_container->createString(slice, loc)); } void JSONBuilder::addNullValue(SourceLoc loc) { _add(JSONValue::makeNull(loc)); } } // namespace Slang