From 55ef35db3a33c85fc2028a7ff52fa4e9e8f20142 Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 19 Jun 2023 15:47:02 +0200 Subject: [PATCH 01/66] identify pin related user actions --- .../gui/user_action/user_action_object.h | 2 -- .../module_details_widget/module_ports_tree.cpp | 14 ++++++++++++++ .../module_details_widget/port_tree_model.cpp | 17 +++++++++++++++++ .../user_action/action_add_items_to_object.cpp | 2 ++ .../src/user_action/action_create_object.cpp | 2 ++ .../src/user_action/action_delete_object.cpp | 2 ++ .../action_remove_items_from_object.cpp | 2 ++ .../src/user_action/action_rename_object.cpp | 4 ++++ .../src/user_action/action_reorder_object.cpp | 2 ++ .../src/user_action/action_set_object_type.cpp | 2 ++ 10 files changed, 47 insertions(+), 2 deletions(-) diff --git a/plugins/gui/include/gui/user_action/user_action_object.h b/plugins/gui/include/gui/user_action/user_action_object.h index 572e608de2f..4658b22d8ab 100644 --- a/plugins/gui/include/gui/user_action/user_action_object.h +++ b/plugins/gui/include/gui/user_action/user_action_object.h @@ -54,8 +54,6 @@ namespace hal Grouping, Netlist, Context, - Pin, - PinGroup, MaxObjectType }; Q_ENUM(ObjectType) diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp index 9d1b73c0400..d5dd75a6ee8 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp @@ -134,10 +134,12 @@ namespace hal return; pinSet.insert(pin->get_id()); } + /* TODO PIN ActionAddItemsToObject* act = new ActionAddItemsToObject(QSet(), QSet(), QSet(), pinSet); act->setObject(UserActionObject(pinGroup->get_id(), UserActionObjectType::PinGroup)); act->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); + */ } }); } @@ -153,10 +155,12 @@ namespace hal auto* group = gNetlist->get_module_by_id(modId)->get_pin_group_by_id(itemId); if (group != nullptr) { + /* TODO PIN ActionRenameObject* renameObj = new ActionRenameObject(ipd.textValue()); renameObj->setObject(UserActionObject(group->get_id(), UserActionObjectType::PinGroup)); renameObj->setParentObject(UserActionObject(modId, UserActionObjectType::Module)); renameObj->exec(); + */ } } }); @@ -164,10 +168,12 @@ namespace hal auto* pinGroup = mod->get_pin_group_by_id(itemId); if (pinGroup != nullptr) { + /* TDOD PIN ActionDeleteObject* delObj = new ActionDeleteObject; delObj->setObject(UserActionObject(pinGroup->get_id(), UserActionObjectType::PinGroup)); delObj->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); delObj->exec(); + */ } }); @@ -193,10 +199,12 @@ namespace hal auto* pin = mod->get_pin_by_id(itemId); if (pin != nullptr) { + /* TODO PIN ActionRenameObject* renameObj = new ActionRenameObject(ipd.textValue()); renameObj->setObject(UserActionObject(pin->get_id(), UserActionObjectType::Pin)); renameObj->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); renameObj->exec(); + */ } } }); @@ -214,10 +222,12 @@ namespace hal if (cbd.exec() == QDialog::Accepted) { + /* TODO PIN ActionSetObjectType* act = new ActionSetObjectType(cbd.textValue()); act->setObject(UserActionObject(pin->get_id(), UserActionObjectType::Pin)); act->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); + */ } }); menu.addAction("Add net to current selection", [this, n]() { @@ -238,10 +248,12 @@ namespace hal for (auto item : selectedPins) pins.insert(mPortModel->getIdOfItem(item)); + /* TODO PIN ActionRemoveItemsFromObject* act = new ActionRemoveItemsFromObject(QSet(), QSet(), QSet(), pins); act->setObject(UserActionObject(mod->get_pin_group_by_id(sameGroup.second)->get_id(), UserActionObjectType::PinGroup)); act->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); + */ }); } @@ -285,6 +297,7 @@ namespace hal return; pins.insert(pin->get_id()); } + /* TODO PIN UserActionCompound* act = new UserActionCompound; act->setUseCreatedObject(); ActionCreateObject* actCreate = new ActionCreateObject(UserActionObjectType::PinGroup, ipd.textValue()); @@ -294,6 +307,7 @@ namespace hal act->addAction(actCreate); act->addAction(actAdd); act->exec(); + */ } }); } diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index 138ed0814e4..73ef3e8b692 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -480,6 +480,7 @@ namespace hal for(const auto &pin : group->get_pins()) pins.insert(pin->get_id()); + /* TODO PIN UserActionCompound* compAct = new UserActionCompound; ActionReorderObject* reorderActHack = new ActionReorderObject(droppedGroup->getOwnRow()); reorderActHack->setObject(UserActionObject(group->get_id(), UserActionObjectType::PinGroup)); @@ -490,6 +491,7 @@ namespace hal compAct->addAction(reorderActHack); compAct->addAction(addAct); compAct->exec(); + */ // too keep the order, ActionAddItemsToObject cannot be executed with all pins, but a ComAction must be created // with many ActionAddItemsToObject that contain a single pin each -> set destroys order of pins in source pingroup setModule(mModule); @@ -505,10 +507,13 @@ namespace hal auto desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; //mModule->move_pin_group(mModule->get_pin_group_by_id(getIdOfItem(droppedGroup)), desiredIdx); + /* TODO PIN ActionReorderObject* reordObj = new ActionReorderObject(desiredIdx); reordObj->setObject(UserActionObject(getIdOfItem(droppedGroup), UserActionObjectType::PinGroup)); reordObj->setParentObject(UserActionObject(mModule->get_id(), UserActionObjectType::Module)); auto ret = reordObj->exec(); + */ + bool ret = true; // TEMP if(ret){ removeItem(droppedGroup); insertItem(droppedGroup, mRootItem, desiredIdx); @@ -520,10 +525,12 @@ namespace hal { mIgnoreEventsFlag = true; QSet e; + /* TODO PIN ActionAddItemsToObject* addAct = new ActionAddItemsToObject(e,e,e, QSet() << getIdOfItem(droppedPin)); addAct->setObject(UserActionObject(getIdOfItem(onDroppedGroup), UserActionObjectType::PinGroup)); addAct->setParentObject(UserActionObject(mModuleId, UserActionObjectType::Module)); addAct->exec(); + */ auto oldParent = droppedPin->getParent(); removeItem(droppedPin); insertItem(droppedPin, onDroppedGroup, onDroppedGroup->getChildCount()); @@ -545,14 +552,17 @@ namespace hal bool bottomEdge = row == onDroppedParent->getChildCount(); desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; // insert item here + /* TODO PIN ActionReorderObject* reorderObj = new ActionReorderObject(desiredIdx); reorderObj->setObject(UserActionObject(getIdOfItem(droppedPin), UserActionObjectType::Pin)); reorderObj->setParentObject(UserActionObject(mModule->get_id(), UserActionObjectType::Module)); reorderObj->exec(); + */ } else // different groups { // reorder action from source group is still needed (for undo action)! + /* TODO PIN UserActionCompound* compAct = new UserActionCompound; ActionReorderObject* reorderActHack = new ActionReorderObject(droppedPin->getOwnRow()); reorderActHack->setObject(UserActionObject(getIdOfItem(droppedPin), UserActionObjectType::Pin)); @@ -567,6 +577,7 @@ namespace hal compAct->addAction(addAct); compAct->addAction(reorderAct); compAct->exec(); + */ } auto oldParent = droppedPin->getParent(); removeItem(droppedPin); @@ -586,10 +597,13 @@ namespace hal mIgnoreEventsFlag = true; auto pinGroup = mModule->get_pin_group_by_id(getIdOfItem(droppedPin->getParent())); QSet e; + /* TODO PIN ActionRemoveItemsFromObject* removeAct = new ActionRemoveItemsFromObject(e,e,e, QSet() << getIdOfItem(droppedPin)); removeAct->setObject(UserActionObject(pinGroup->get_id(), UserActionObjectType::PinGroup)); removeAct->setParentObject(UserActionObject(mModuleId, UserActionObjectType::Module)); bool ret = removeAct->exec(); + */ + bool ret = true; // TEMP // must search for the created group (must secure way is to serve for a group that contains the pin id) // and then move that group (the source group cannot be empty after the pin was removed -> canDropMimeData) if(ret) // can fail if the pins name that one wants to remove has the same name as another group that already exists @@ -604,11 +618,14 @@ namespace hal pinGroupItem->setAdditionalData(keyId, newGroup->get_id()); int pos = mRootItem->getChildCount(); // or query current index + + /* TODO PIN ActionReorderObject* reordObj = new ActionReorderObject(row); reordObj->setObject(UserActionObject(newGroup->get_id(), UserActionObjectType::PinGroup)); reordObj->setParentObject(UserActionObject(mModule->get_id(), UserActionObjectType::Module)); if(reordObj->exec()) pos = row; + */ insertItem(pinGroupItem, mRootItem, pos); removeItem(droppedPin); diff --git a/plugins/gui/src/user_action/action_add_items_to_object.cpp b/plugins/gui/src/user_action/action_add_items_to_object.cpp index 44b87bba1fe..25a201b5c0c 100644 --- a/plugins/gui/src/user_action/action_add_items_to_object.cpp +++ b/plugins/gui/src/user_action/action_add_items_to_object.cpp @@ -173,6 +173,7 @@ namespace hal else return false; break; + /* TODO PIN case UserActionObjectType::PinGroup: { if (mPins.empty()) return true; @@ -257,6 +258,7 @@ namespace hal } } break; + */ default: return false; } diff --git a/plugins/gui/src/user_action/action_create_object.cpp b/plugins/gui/src/user_action/action_create_object.cpp index 2a73b467ae0..8a501f44b13 100644 --- a/plugins/gui/src/user_action/action_create_object.cpp +++ b/plugins/gui/src/user_action/action_create_object.cpp @@ -63,6 +63,7 @@ namespace hal switch (mObject.type()) { + /* TODO PIN case UserActionObjectType::PinGroup: { Module* parentModule = gNetlist->get_module_by_id(mParentObject.id()); @@ -77,6 +78,7 @@ namespace hal else return false; } + */ break; case UserActionObjectType::Module: diff --git a/plugins/gui/src/user_action/action_delete_object.cpp b/plugins/gui/src/user_action/action_delete_object.cpp index b26e214e8d5..323a6b06125 100644 --- a/plugins/gui/src/user_action/action_delete_object.cpp +++ b/plugins/gui/src/user_action/action_delete_object.cpp @@ -41,6 +41,7 @@ namespace hal switch (mObject.type()) { + /* TODO PIN case UserActionObjectType::PinGroup: { mod = gNetlist->get_module_by_id(mParentObject.id()); auto* pinGroup = mod->get_pin_group_by_id(mObject.id()); @@ -64,6 +65,7 @@ namespace hal return false; } break; + */ case UserActionObjectType::Module: mod = gNetlist->get_module_by_id(mObject.id()); if (mod) diff --git a/plugins/gui/src/user_action/action_remove_items_from_object.cpp b/plugins/gui/src/user_action/action_remove_items_from_object.cpp index 00719d3bdc0..79212b7e558 100644 --- a/plugins/gui/src/user_action/action_remove_items_from_object.cpp +++ b/plugins/gui/src/user_action/action_remove_items_from_object.cpp @@ -116,6 +116,7 @@ namespace hal else return false; break; + /* TODO PIN case UserActionObjectType::PinGroup: { auto mod = gNetlist->get_module_by_id(mParentObject.id()); if (mod) @@ -162,6 +163,7 @@ namespace hal } } break; + */ default: return false; } diff --git a/plugins/gui/src/user_action/action_rename_object.cpp b/plugins/gui/src/user_action/action_rename_object.cpp index d5f6d3e35cb..2e8a080cd8c 100644 --- a/plugins/gui/src/user_action/action_rename_object.cpp +++ b/plugins/gui/src/user_action/action_rename_object.cpp @@ -104,6 +104,7 @@ namespace hal return false; } break; + /* TODO PIN case UserActionObjectType::Pin: { mod = gNetlist->get_module_by_id(mParentObject.id()); if (!mod) @@ -138,15 +139,18 @@ namespace hal mod->set_pin_group_name(pinGroup, mNewName.toStdString()); } break; + */ default: return false; } ActionRenameObject* undo = new ActionRenameObject(oldName); undo->setObject(mObject); + /* TODO PIN if (mObject.type() == UserActionObjectType::Pin || mObject.type() == UserActionObjectType::PinGroup) { undo->setParentObject(mParentObject); } + */ mUndoAction = undo; return UserAction::exec(); } diff --git a/plugins/gui/src/user_action/action_reorder_object.cpp b/plugins/gui/src/user_action/action_reorder_object.cpp index 8db33f3f89c..cddb10050c4 100644 --- a/plugins/gui/src/user_action/action_reorder_object.cpp +++ b/plugins/gui/src/user_action/action_reorder_object.cpp @@ -23,6 +23,7 @@ namespace hal bool ActionReorderObject::exec() { + /* TODO PIN int oldIndex = -1; switch (mObject.type()) { @@ -86,6 +87,7 @@ namespace hal undo->setParentObject(mParentObject); mUndoAction = undo; } + */ return UserAction::exec(); } diff --git a/plugins/gui/src/user_action/action_set_object_type.cpp b/plugins/gui/src/user_action/action_set_object_type.cpp index f86cb5d2f41..00c52ebc305 100644 --- a/plugins/gui/src/user_action/action_set_object_type.cpp +++ b/plugins/gui/src/user_action/action_set_object_type.cpp @@ -49,6 +49,7 @@ namespace hal Module* mod; switch (mObject.type()) { + /* TODO PIN case UserActionObjectType::Pin: //only possible to change module pin types { //should also check if its a valid type? (enum_from_string default value doesnt help @@ -74,6 +75,7 @@ namespace hal } } break; + */ case UserActionObjectType::Module: mod = gNetlist->get_module_by_id(mObject.id()); if (mod != nullptr) From ea258b724ba83c8c21641464627d5a7d8b69dd40 Mon Sep 17 00:00:00 2001 From: joern274 Date: Fri, 30 Jun 2023 11:04:05 +0200 Subject: [PATCH 02/66] class ActionPingroup added --- .../include/gui/user_action/action_pingroup.h | 112 +++++++ .../module_details_widget/port_tree_model.cpp | 160 +++++----- .../gui/src/user_action/action_pingroup.cpp | 280 ++++++++++++++++++ 3 files changed, 467 insertions(+), 85 deletions(-) create mode 100644 plugins/gui/include/gui/user_action/action_pingroup.h create mode 100644 plugins/gui/src/user_action/action_pingroup.cpp diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h new file mode 100644 index 00000000000..8a7b011faa6 --- /dev/null +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -0,0 +1,112 @@ +// MIT License +// +// Copyright (c) 2019 Ruhr University Bochum, Chair for Embedded Security. All Rights reserved. +// Copyright (c) 2019 Marc Fyrbiak, Sebastian Wallat, Max Hoffmann ("ORIGINAL AUTHORS"). All rights reserved. +// Copyright (c) 2021 Max Planck Institute for Security and Privacy. All Rights reserved. +// Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#include "user_action.h" +#include +#include + +namespace hal +{ + class PinAction : public QObject + { + Q_OBJECT + public: + enum Type { None, Create, Delete, MovePin, MoveGroup, Rename, TypeChange, MaxAction }; + Q_ENUM(Type) + public: + static QString toString(Type tp); + static Type fromString(const QString& s); + }; + + /** + * @ingroup user_action + * @brief Pingroup user actions + * + * Action depends on PinAction::Type: + * + * Create: + * Pingroup with given name gets created. + * Pins listed in mPinIds get moved into new group + * Id of created group returned as targetGroupId() + * + * Delete: + * Pingroup ID=mSourceGroupId gets deleted. + * Pins in group are stored for undo command + * + * MovePin: + * Must use move constructor with mandatory arguments pinId and targetIndex + * One out of targetIndex (existing pingroup) or name (create new pingroup) + * must be given to indicate destination + * + * MoveGroup: + * Move Group identified by sourceGroupId to new targetIndex + */ + class ActionPingroup : public UserAction + { + private: + QList mPinActions; + QList mPinIds; + u32 mSourceGroupId; + u32 mTargetGroupId; + int mPinOrderNo; + int mGroupOrderNo; + QString mName; + public: + /** + * Action Constructor. + * + * @param type - The UserActionObjectType of the item that should be created (default type: None) + * @param objName - The name of the object to create (default name: ""). + */ + ActionPingroup(PinAction::Type action = PinAction::None, u32 pingroupId = 0, const QString& name=QString()); + ActionPingroup(u32 pinId, int pinIndex, u32 tgtgroupId=0, const QString& name=QString(), int grpIndex=-1); // action = MovePin + bool exec() override; + QString tagname() const override; + void writeToXml(QXmlStreamWriter& xmlOut) const override; + void readFromXml(QXmlStreamReader& xmlIn) override; + void addToHash(QCryptographicHash& cryptoHash) const override; + void setPinIds(const QList& ids) { mPinIds = ids; } + void addPinAction(PinAction::Type action) { mPinActions.prepend(action); } + void setSourceGroupId(u32 id) { mSourceGroupId = id; } + void setTargetGroupId(u32 id) { mTargetGroupId = id; } + void setPinOrderNo(int inx) { mPinOrderNo = inx; } + void setGroupOrderNo(int inx) { mGroupOrderNo = inx; } + void setName(const QString& name) { mName = name; } + u32 targetGroupId() const { return mTargetGroupId; } + }; + + /** + * @ingroup user_action + * @brief UserActionFactory for ActionPingroup + */ + class ActionPingroupFactory : public UserActionFactory + { + public: + ActionPingroupFactory(); + UserAction* newAction() const; + static ActionPingroupFactory* sFactory; + }; +} diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index 73ef3e8b692..97b27bf9f2c 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -3,7 +3,7 @@ #include "gui/basic_tree_model/tree_item.h" #include "gui/gui_globals.h" #include "gui/input_dialog/input_dialog.h" -#include "gui/user_action/action_add_items_to_object.h" +#include "gui/user_action/action_pingroup.h" #include "gui/user_action/action_remove_items_from_object.h" #include "gui/user_action/action_reorder_object.h" #include "gui/user_action/user_action_compound.h" @@ -114,23 +114,33 @@ namespace hal if(type == "group") { if(!parentItem) - //qDebug() << "group was dropped between groups... with row: " << row; //check in canDropMine if its not an adjacent row? + { + qDebug() << "group was dropped between groups... with row: " << row; //check in canDropMine if its not an adjacent row? dndGroupBetweenGroup(droppedItem, row); + } else - //qDebug() << "group was dropped on a group?"; + { + qDebug() << "group was dropped on a group?"; dndGroupOnGroup(droppedItem, parentItem); + } } else { if(!parentItem) - //qDebug() << "pin was dropped between groups on row " << row; + { + qDebug() << "pin was dropped between groups on row " << row; dndPinBetweenGroup(droppedItem, row); + } else if(row != -1) - //qDebug() << "pin was dropped between pins"; + { + qDebug() << "pin was dropped between pins"; dndPinBetweenPin(droppedItem, parentItem, row); + } else - //qDebug() << "pin was dropped on a group..."; + { + qDebug() << "pin was dropped on a group..."; dndPinOnGroup(droppedItem, parentItem); + } } return true; @@ -475,23 +485,30 @@ namespace hal // InputDialog ipd("Name of new group", "Name of new group:", onDroppedGroup->getData(sNameColumn).toString()); // if(ipd.exec() == QDialog::Rejected) return false; mIgnoreEventsFlag = true; - QSet pins, e; - auto group = mModule->get_pin_group_by_id(getIdOfItem(droppedGroup)); - for(const auto &pin : group->get_pins()) - pins.insert(pin->get_id()); - - /* TODO PIN - UserActionCompound* compAct = new UserActionCompound; - ActionReorderObject* reorderActHack = new ActionReorderObject(droppedGroup->getOwnRow()); - reorderActHack->setObject(UserActionObject(group->get_id(), UserActionObjectType::PinGroup)); - reorderActHack->setParentObject(UserActionObject(mModuleId, UserActionObjectType::Module)); - ActionAddItemsToObject* addAct = new ActionAddItemsToObject(e,e,e, pins); - addAct->setObject(UserActionObject(getIdOfItem(onDroppedGroup), UserActionObjectType::PinGroup)); - addAct->setParentObject(UserActionObject(mModuleId, UserActionObjectType::Module)); - compAct->addAction(reorderActHack); - compAct->addAction(addAct); - compAct->exec(); - */ + QList pins; + auto srcgroup = mModule->get_pin_group_by_id(getIdOfItem(droppedGroup)); + for(const auto &pin : srcgroup->get_pins()) + pins.append(pin->get_id()); + if (pins.isEmpty()) return; // no pins to move + + auto tgtgroup = mModule->get_pin_group_by_id(getIdOfItem(onDroppedGroup)); + int ntgt = tgtgroup->size(); + if (pins.size()>1) + { + UserActionCompound* compAct = new UserActionCompound; + compAct->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); + compAct->setUseCreatedObject(); + for (u32 pinId : pins) + compAct->addAction(new ActionPingroup(pinId,ntgt++,tgtgroup->get_id())); + compAct->exec(); + } + else + { + ActionPingroup* act = new ActionPingroup(pins.at(0),ntgt,tgtgroup->get_id()); + act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); + act->exec(); + } + // too keep the order, ActionAddItemsToObject cannot be executed with all pins, but a ComAction must be created // with many ActionAddItemsToObject that contain a single pin each -> set destroys order of pins in source pingroup setModule(mModule); @@ -506,15 +523,11 @@ namespace hal bool bottomEdge = row == mRootItem->getChildCount(); auto desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; - //mModule->move_pin_group(mModule->get_pin_group_by_id(getIdOfItem(droppedGroup)), desiredIdx); - /* TODO PIN - ActionReorderObject* reordObj = new ActionReorderObject(desiredIdx); - reordObj->setObject(UserActionObject(getIdOfItem(droppedGroup), UserActionObjectType::PinGroup)); - reordObj->setParentObject(UserActionObject(mModule->get_id(), UserActionObjectType::Module)); - auto ret = reordObj->exec(); - */ - bool ret = true; // TEMP - if(ret){ + ActionPingroup* act = new ActionPingroup(PinAction::MoveGroup,(u32)getIdOfItem(droppedGroup)); + act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); + act->setPinOrderNo(desiredIdx); + bool ok = act->exec(); + if(ok){ removeItem(droppedGroup); insertItem(droppedGroup, mRootItem, desiredIdx); } @@ -524,13 +537,12 @@ namespace hal void ModulePinsTreeModel::dndPinOnGroup(TreeItem *droppedPin, TreeItem *onDroppedGroup) { mIgnoreEventsFlag = true; - QSet e; - /* TODO PIN - ActionAddItemsToObject* addAct = new ActionAddItemsToObject(e,e,e, QSet() << getIdOfItem(droppedPin)); - addAct->setObject(UserActionObject(getIdOfItem(onDroppedGroup), UserActionObjectType::PinGroup)); - addAct->setParentObject(UserActionObject(mModuleId, UserActionObjectType::Module)); - addAct->exec(); - */ + u32 pinId = getIdOfItem(droppedPin); + + int inx = onDroppedGroup->getChildCount(); + ActionPingroup* act = new ActionPingroup(pinId,inx, getIdOfItem(onDroppedGroup)); + act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); + act->exec(); auto oldParent = droppedPin->getParent(); removeItem(droppedPin); insertItem(droppedPin, onDroppedGroup, onDroppedGroup->getChildCount()); @@ -552,33 +564,11 @@ namespace hal bool bottomEdge = row == onDroppedParent->getChildCount(); desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; // insert item here - /* TODO PIN - ActionReorderObject* reorderObj = new ActionReorderObject(desiredIdx); - reorderObj->setObject(UserActionObject(getIdOfItem(droppedPin), UserActionObjectType::Pin)); - reorderObj->setParentObject(UserActionObject(mModule->get_id(), UserActionObjectType::Module)); - reorderObj->exec(); - */ - } - else // different groups - { - // reorder action from source group is still needed (for undo action)! - /* TODO PIN - UserActionCompound* compAct = new UserActionCompound; - ActionReorderObject* reorderActHack = new ActionReorderObject(droppedPin->getOwnRow()); - reorderActHack->setObject(UserActionObject(getIdOfItem(droppedPin), UserActionObjectType::Pin)); - reorderActHack->setParentObject(UserActionObject(mModuleId, UserActionObjectType::Module)); - ActionAddItemsToObject* addAct = new ActionAddItemsToObject(QSet(), QSet(), QSet(), QSet() << getIdOfItem(droppedPin)); - addAct->setObject(UserActionObject(getIdOfItem(onDroppedParent), UserActionObjectType::PinGroup)); - addAct->setParentObject(UserActionObject(mModuleId, UserActionObjectType::Module)); - ActionReorderObject* reorderAct = new ActionReorderObject(row); - reorderAct->setObject(UserActionObject(getIdOfItem(droppedPin), UserActionObjectType::Pin)); - reorderAct->setParentObject(UserActionObject(mModule->get_id(), UserActionObjectType::Module)); - compAct->addAction(reorderActHack); - compAct->addAction(addAct); - compAct->addAction(reorderAct); - compAct->exec(); - */ } + + ActionPingroup* act = new ActionPingroup(getIdOfItem(droppedPin),desiredIdx,getIdOfItem(onDroppedParent)); + act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); + act->exec(); auto oldParent = droppedPin->getParent(); removeItem(droppedPin); insertItem(droppedPin, onDroppedParent, desiredIdx); @@ -595,19 +585,27 @@ namespace hal // row is needed for when groups can change its order within the module Q_UNUSED(row) mIgnoreEventsFlag = true; - auto pinGroup = mModule->get_pin_group_by_id(getIdOfItem(droppedPin->getParent())); - QSet e; - /* TODO PIN - ActionRemoveItemsFromObject* removeAct = new ActionRemoveItemsFromObject(e,e,e, QSet() << getIdOfItem(droppedPin)); - removeAct->setObject(UserActionObject(pinGroup->get_id(), UserActionObjectType::PinGroup)); - removeAct->setParentObject(UserActionObject(mModuleId, UserActionObjectType::Module)); - bool ret = removeAct->exec(); - */ - bool ret = true; // TEMP - // must search for the created group (must secure way is to serve for a group that contains the pin id) - // and then move that group (the source group cannot be empty after the pin was removed -> canDropMimeData) - if(ret) // can fail if the pins name that one wants to remove has the same name as another group that already exists + + auto pinToMove = mModule->get_pin_by_id(getIdOfItem(droppedPin)); + if (!pinToMove) return; + + QString groupName = QString::fromStdString(pinToMove->get_name()); + QString baseName = groupName; + int cnt = 2; + while (mModule->get_pin_group_by_name(groupName.toStdString())) + // pin group name already exists + groupName = QString("%1_%2").arg(baseName).arg(cnt++); + + ActionPingroup* actMovePin = new ActionPingroup(pinToMove->get_id(),0,0,groupName); + actMovePin->setObject(UserActionObject(mModuleId, UserActionObjectType::Module)); + bool ok = actMovePin->exec(); + if (ok && actMovePin->targetGroupId()) { + ActionPingroup* actMoveGroup = new ActionPingroup(PinAction::MoveGroup,actMovePin->targetGroupId()); + actMoveGroup->setObject(UserActionObject(mModuleId, UserActionObjectType::Module)); + actMoveGroup->setPinOrderNo(row); + actMoveGroup->exec(); + auto newGroup = mModule->get_pin_by_id(getIdOfItem(droppedPin))->get_group().first; auto pinGroupName = QString::fromStdString(newGroup->get_name()); auto pinGroupDirection = QString::fromStdString(enum_to_string((newGroup->get_direction()))); @@ -619,14 +617,6 @@ namespace hal int pos = mRootItem->getChildCount(); // or query current index - /* TODO PIN - ActionReorderObject* reordObj = new ActionReorderObject(row); - reordObj->setObject(UserActionObject(newGroup->get_id(), UserActionObjectType::PinGroup)); - reordObj->setParentObject(UserActionObject(mModule->get_id(), UserActionObjectType::Module)); - if(reordObj->exec()) - pos = row; - */ - insertItem(pinGroupItem, mRootItem, pos); removeItem(droppedPin); insertItem(droppedPin, pinGroupItem, 0); diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp new file mode 100644 index 00000000000..d0c4f3ae54e --- /dev/null +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -0,0 +1,280 @@ +#include "gui/user_action/action_pingroup.h" +#include "gui/gui_globals.h" +#include "hal_core/netlist/grouping.h" +#include "gui/grouping/grouping_manager_widget.h" +#include "gui/graph_widget/contexts/graph_context.h" +#include + +namespace hal +{ + QString PinAction::toString(PinAction::Type tp) + { + QMetaEnum me = QMetaEnum::fromType(); + return QString(me.key(tp)); + } + + PinAction::Type PinAction::fromString(const QString &s) + { + QMetaEnum me = QMetaEnum::fromType(); + for (int t = None; t < MaxAction; t++) + if (s == me.key(t)) + { + return static_cast(t); + } + return None; + } + + ActionPingroupFactory::ActionPingroupFactory() + : UserActionFactory("Pingroup") {;} + + ActionPingroupFactory* ActionPingroupFactory::sFactory = new ActionPingroupFactory; + + UserAction* ActionPingroupFactory::newAction() const + { + return new ActionPingroup; + } + + QString ActionPingroup::tagname() const + { + return ActionPingroupFactory::sFactory->tagname(); + } + + ActionPingroup::ActionPingroup(PinAction::Type action, u32 pingroupId, const QString &name) + : mSourceGroupId(pingroupId), mTargetGroupId(pingroupId), mPinOrderNo(-1), mGroupOrderNo(-1), mName(name) + { + switch (action) + { + case PinAction::None: + break; + case PinAction::MovePin: + // use move constructor + break; + default: + mPinActions.append(action); + } + } + + ActionPingroup::ActionPingroup(u32 pinId, int pinIndex, u32 tgtgroupId, const QString& name, int grpIndex) + : mSourceGroupId(0), mTargetGroupId(tgtgroupId), mPinOrderNo(pinIndex), mGroupOrderNo(grpIndex), mName(name) + { + mPinIds.append(pinId); + mPinActions.append(PinAction::MovePin); + } + + void ActionPingroup::addToHash(QCryptographicHash &cryptoHash) const + { + for (PinAction::Type tp : mPinActions) + cryptoHash.addData((char*)(&tp),sizeof(tp)); + for (u32 pid: mPinIds) + cryptoHash.addData((char*)(&pid),sizeof(pid)); + cryptoHash.addData((char*)(&mSourceGroupId),sizeof(mSourceGroupId)); + cryptoHash.addData((char*)(&mTargetGroupId),sizeof(mTargetGroupId)); + cryptoHash.addData((char*)(&mPinOrderNo),sizeof(mPinOrderNo)); + cryptoHash.addData((char*)(&mGroupOrderNo),sizeof(mGroupOrderNo)); + } + + void ActionPingroup::writeToXml(QXmlStreamWriter& xmlOut) const + { + //todo: remove parentId, switch entirely to parentObject + if (!mPinActions.isEmpty()) + { + QString s; + for (PinAction::Type tp : mPinActions) + { + if (!s.isEmpty()) s += ','; + s += PinAction::toString(tp); + } + xmlOut.writeTextElement("pinactions", s); + } + if (!mPinIds.isEmpty()) + { + QString s; + for (u32 pinId : mPinIds) + { + if (!s.isEmpty()) s += ','; + s += QString::number(pinId); + } + xmlOut.writeTextElement("pinids", s); + } + if (mSourceGroupId) + xmlOut.writeTextElement("srcgroupid", QString::number(mSourceGroupId)); + if (mTargetGroupId) + xmlOut.writeTextElement("tgtgroupid", QString::number(mTargetGroupId)); + if (mPinOrderNo >= 0) + xmlOut.writeTextElement("pinorder", QString::number(mPinOrderNo)); + if (mGroupOrderNo >= 0) + xmlOut.writeTextElement("grporder", QString::number(mGroupOrderNo)); + } + + void ActionPingroup::readFromXml(QXmlStreamReader& xmlIn) + { + while (xmlIn.readNextStartElement()) + { + //todo: emove parentId, switch entirely to parentObject + readParentObjectFromXml(xmlIn); + if (xmlIn.name() == "pinactions") + { + for (QString pinact : xmlIn.readElementText().split(',')) + mPinActions.append(PinAction::fromString(pinact)); + } + if (xmlIn.name() == "pinids") + { + for (QString pinid : xmlIn.readElementText().split(',')) + mPinIds.append(pinid.toInt()); + } + if (xmlIn.name() == "srcgroupid") + mSourceGroupId = xmlIn.readElementText().toInt(); + if (xmlIn.name() == "tgtgroupid") + mTargetGroupId = xmlIn.readElementText().toInt(); + if (xmlIn.name() == "pinorder") + mPinOrderNo = xmlIn.readElementText().toInt(); + if (xmlIn.name() == "grporder") + mGroupOrderNo = xmlIn.readElementText().toInt(); + } + } + + bool ActionPingroup::exec() + { + if (mObject.type() != UserActionObjectType::Module) + return false; + + Module* parentModule = gNetlist->get_module_by_id(mObject.id()); + if (!parentModule) + return false; + + ActionPingroup* undo = nullptr; + + if (mPinActions.size()==1 && mPinActions.at(0) == PinAction::MovePin) + { + auto* pinToMove = parentModule->get_pin_by_id(mPinIds.at(0)); + if (!pinToMove) return false; + auto* srcgrp = pinToMove->get_group().first; + if (!srcgrp) return false; + int currentIndex = pinToMove->get_group().second; + mSourceGroupId = srcgrp->get_id(); + undo = new ActionPingroup(pinToMove->get_id(), currentIndex, mSourceGroupId, QString::fromStdString(srcgrp->get_name())); + if (srcgrp->get_pins().size() == 1) mPinActions.append(PinAction::Delete); + if (mTargetGroupId) + { + auto* tgtgrp = parentModule->get_pin_group_by_id(mTargetGroupId); + if (!tgtgrp) + tgtgrp = parentModule->get_pin_group_by_name(mName.toStdString()); + if (!tgtgrp) + mTargetGroupId = 0; + } + else + { + auto* tgtgrp = parentModule->get_pin_group_by_name(mName.toStdString()); + if (tgtgrp) + mTargetGroupId = tgtgrp->get_id(); + } + if (!mTargetGroupId) + { + // target group does not exist, create it + if (mName.isEmpty()) + mName = QString::fromStdString(pinToMove->get_name()); + mPinActions.replace(0,PinAction::Create); // create will place pins in new group + } + } + + + for (PinAction::Type act : mPinActions) + { + switch (act) + { + case PinAction::None: + break; + case PinAction::Create: + { + auto res = parentModule->create_pin_group(mName.toStdString()); + if(res.is_error()) + return false; + mTargetGroupId = res.get()->get_id(); + if (!mPinIds.isEmpty()) + { + auto* pgrp = parentModule->get_pin_group_by_id(mTargetGroupId); + for (u32 pinId : mPinIds) + { + auto pin = parentModule->get_pin_by_id(pinId); + if (pin) pgrp->assign_pin(pin).is_ok(); + } + } + if (!undo) + undo = new ActionPingroup(PinAction::Delete, mTargetGroupId); + break; + } + case PinAction::Delete: + { + auto* pgrp = parentModule->get_pin_group_by_id(mSourceGroupId); + QList pins; + for (const auto& pin : pgrp->get_pins()) + { + pins.append(pin->get_id()); + } + if (!undo) + { + undo = new ActionPingroup(PinAction::Create, pgrp->get_id(), QString::fromStdString(pgrp->get_name())); + undo->setPinIds(pins); + } + break; + } + case PinAction::MovePin: + { + auto* tgtgrp = parentModule->get_pin_group_by_id(mTargetGroupId); + auto* pinToMove = parentModule->get_pin_by_id(mPinIds.at(0)); + if (!tgtgrp || !pinToMove) return false; + auto* srcgrp = pinToMove->get_group().first; + + if (srcgrp != tgtgrp) + { + if (tgtgrp->assign_pin(pinToMove).is_ok()) return false; + } + tgtgrp->move_pin(pinToMove,mPinOrderNo).is_ok(); + break; + } + case PinAction::MoveGroup: + { + PinGroup* srcgrp = nullptr; + int currentIndex = 0; + for (PinGroup* pgrp : parentModule->get_pin_groups()) + { + if (pgrp->get_id() == mSourceGroupId) + { + srcgrp = pgrp; + break; + } + ++ currentIndex; + } + if (!srcgrp) return false; + if (parentModule->move_pin_group(srcgrp,mPinOrderNo).is_error()) return false; + if (!undo) + { + undo = new ActionPingroup(PinAction::MoveGroup, mSourceGroupId); + undo->setPinOrderNo(currentIndex); + } + } + case PinAction::Rename: + { + auto* pgrp = parentModule->get_pin_group_by_id(mTargetGroupId); + if (!pgrp) return false; + QString oldName = QString::fromStdString(pgrp->get_name()); + pgrp->set_name(mName.toStdString()); + if (undo) + { + undo->addPinAction(PinAction::Rename); + undo->setName(oldName); + } + else + undo = new ActionPingroup(PinAction::Rename, pgrp->get_id(), oldName); + break; + } + case PinAction::TypeChange: + break; + default: + break; + } + } + mUndoAction = undo; + return UserAction::exec(); + } +} From b712175a269d5ab9a4a4e6fc892701dec57a4f2b Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 5 Jul 2023 19:56:08 +0200 Subject: [PATCH 03/66] Implementation of ActionPingroup --- .../include/gui/user_action/action_pingroup.h | 23 +++- .../module_ports_tree.cpp | 79 +++++------- .../module_details_widget/port_tree_model.cpp | 3 +- .../gui/src/user_action/action_pingroup.cpp | 116 +++++++++++++----- 4 files changed, 134 insertions(+), 87 deletions(-) diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index 8a7b011faa6..9a1e27e8c84 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -25,22 +25,25 @@ #pragma once #include "user_action.h" +#include "hal_core/netlist/module.h" #include #include namespace hal { + class PinAction : public QObject { Q_OBJECT public: - enum Type { None, Create, Delete, MovePin, MoveGroup, Rename, TypeChange, MaxAction }; + enum Type { None, Create, DeleteGroup, RemovePin, MovePin, MoveGroup, RenamePin, RenameGroup, TypeChange, MaxAction }; Q_ENUM(Type) public: static QString toString(Type tp); static Type fromString(const QString& s); }; + int pinGroupIndex(const Module* mod, const PinGroup* pgrp); /** * @ingroup user_action * @brief Pingroup user actions @@ -49,20 +52,27 @@ namespace hal * * Create: * Pingroup with given name gets created. - * Pins listed in mPinIds get moved into new group + * Pins listed in pinIds get moved into new group * Id of created group returned as targetGroupId() * - * Delete: - * Pingroup ID=mSourceGroupId gets deleted. + * DeleteGroup: + * Pingroup ID=sourceGroupId gets deleted. * Pins in group are stored for undo command * + * RemovePin: + * Remove pin from sourceGroup + * * MovePin: - * Must use move constructor with mandatory arguments pinId and targetIndex + * Must use move constructor with mandatory arguments pinId and pinOrderNo * One out of targetIndex (existing pingroup) or name (create new pingroup) * must be given to indicate destination * * MoveGroup: - * Move Group identified by sourceGroupId to new targetIndex + * Move Group identified by sourceGroupId to position groupOrderNo + * RenamePin: + * Set new name to first pin in pinIds + * RenameGroup: + * Set new name to group identified by targetGroupId */ class ActionPingroup : public UserAction { @@ -89,6 +99,7 @@ namespace hal void readFromXml(QXmlStreamReader& xmlIn) override; void addToHash(QCryptographicHash& cryptoHash) const override; void setPinIds(const QList& ids) { mPinIds = ids; } + void setPinId(u32 id) { mPinIds.clear(); mPinIds.append(id); } void addPinAction(PinAction::Type action) { mPinActions.prepend(action); } void setSourceGroupId(u32 id) { mSourceGroupId = id; } void setTargetGroupId(u32 id) { mTargetGroupId = id; } diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp index d5dd75a6ee8..96475ac7079 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp @@ -12,6 +12,7 @@ #include "gui/user_action/action_remove_items_from_object.h" #include "gui/user_action/action_rename_object.h" #include "gui/user_action/action_set_object_type.h" +#include "gui/user_action/action_pingroup.h" #include "gui/user_action/user_action_compound.h" #include "hal_core/netlist/gate_library/enums/pin_direction.h" #include "hal_core/netlist/gate_library/enums/pin_type.h" @@ -123,7 +124,7 @@ namespace hal PingroupSelectorDialog psd("Pingroup selector", "Select pingroup", mod, false); if (psd.exec() == QDialog::Accepted) { - QSet pinSet; + QList pins; auto* pinGroup = mod->get_pin_group_by_id(psd.getSelectedGroupId()); if (pinGroup == nullptr) return; @@ -132,14 +133,12 @@ namespace hal auto* pin = mod->get_pin_by_id(mPortModel->getIdOfItem(item)); if (pin == nullptr) return; - pinSet.insert(pin->get_id()); + pins.append(pin->get_id()); } - /* TODO PIN - ActionAddItemsToObject* act = new ActionAddItemsToObject(QSet(), QSet(), QSet(), pinSet); - act->setObject(UserActionObject(pinGroup->get_id(), UserActionObjectType::PinGroup)); - act->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); + ActionPingroup* act = new ActionPingroup(PinAction::MovePin,pinGroup->get_id()); + act->setPinIds(pins); + act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); - */ } }); } @@ -155,12 +154,9 @@ namespace hal auto* group = gNetlist->get_module_by_id(modId)->get_pin_group_by_id(itemId); if (group != nullptr) { - /* TODO PIN - ActionRenameObject* renameObj = new ActionRenameObject(ipd.textValue()); - renameObj->setObject(UserActionObject(group->get_id(), UserActionObjectType::PinGroup)); - renameObj->setParentObject(UserActionObject(modId, UserActionObjectType::Module)); - renameObj->exec(); - */ + ActionPingroup* act = new ActionPingroup(PinAction::RenameGroup,itemId,ipd.textValue()); + act->setObject(UserActionObject(modId, UserActionObjectType::Module)); + act->exec(); } } }); @@ -168,12 +164,9 @@ namespace hal auto* pinGroup = mod->get_pin_group_by_id(itemId); if (pinGroup != nullptr) { - /* TDOD PIN - ActionDeleteObject* delObj = new ActionDeleteObject; - delObj->setObject(UserActionObject(pinGroup->get_id(), UserActionObjectType::PinGroup)); - delObj->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); - delObj->exec(); - */ + ActionPingroup* act = new ActionPingroup(PinAction::DeleteGroup,(u32)itemId); + act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); + act->exec(); } }); @@ -199,12 +192,10 @@ namespace hal auto* pin = mod->get_pin_by_id(itemId); if (pin != nullptr) { - /* TODO PIN - ActionRenameObject* renameObj = new ActionRenameObject(ipd.textValue()); - renameObj->setObject(UserActionObject(pin->get_id(), UserActionObjectType::Pin)); - renameObj->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); - renameObj->exec(); - */ + ActionPingroup* act = new ActionPingroup(PinAction::RenamePin,0,ipd.textValue()); + act->setPinId(pin->get_id()); + act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); + act->exec(); } } }); @@ -222,12 +213,10 @@ namespace hal if (cbd.exec() == QDialog::Accepted) { - /* TODO PIN - ActionSetObjectType* act = new ActionSetObjectType(cbd.textValue()); - act->setObject(UserActionObject(pin->get_id(), UserActionObjectType::Pin)); - act->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); + ActionPingroup* act = new ActionPingroup(PinAction::TypeChange,0,cbd.textValue()); + act->setPinId(pin->get_id()); + act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); - */ } }); menu.addAction("Add net to current selection", [this, n]() { @@ -244,16 +233,14 @@ namespace hal if (sameGroup.first && mod->get_pin_group_by_id(sameGroup.second)->size() > 1) { menu.addAction("Remove selection from group", [this, selectedPins, mod, sameGroup]() { - QSet pins; + QList pins; for (auto item : selectedPins) - pins.insert(mPortModel->getIdOfItem(item)); + pins.append(mPortModel->getIdOfItem(item)); - /* TODO PIN - ActionRemoveItemsFromObject* act = new ActionRemoveItemsFromObject(QSet(), QSet(), QSet(), pins); - act->setObject(UserActionObject(mod->get_pin_group_by_id(sameGroup.second)->get_id(), UserActionObjectType::PinGroup)); - act->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); + ActionPingroup* act = new ActionPingroup(PinAction::RemovePin,mod->get_pin_group_by_id(sameGroup.second)->get_id()); + act->setPinIds(pins); + act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); - */ }); } @@ -288,26 +275,20 @@ namespace hal InputDialog ipd("Pingroup name", "New pingroup name", "ExampleName"); if (ipd.exec() == QDialog::Accepted && !ipd.textValue().isEmpty()) { - QSet pins; + QList pins; auto mod = gNetlist->get_module_by_id(modId); for (auto item : selectedPins) { auto* pin = mod->get_pin_by_id(mPortModel->getIdOfItem(item)); if (pin == nullptr) return; - pins.insert(pin->get_id()); + pins.append(pin->get_id()); } - /* TODO PIN - UserActionCompound* act = new UserActionCompound; - act->setUseCreatedObject(); - ActionCreateObject* actCreate = new ActionCreateObject(UserActionObjectType::PinGroup, ipd.textValue()); - actCreate->setParentObject(UserActionObject(modId, UserActionObjectType::Module)); - ActionAddItemsToObject* actAdd = new ActionAddItemsToObject(QSet(), QSet(), QSet(), pins); - actAdd->setUsedInCreateContext(); - act->addAction(actCreate); - act->addAction(actAdd); + + ActionPingroup* act = new ActionPingroup(PinAction::Create,0,ipd.textValue()); + act->setPinIds(pins); + act->setObject(UserActionObject(modId, UserActionObjectType::Module)); act->exec(); - */ } }); } diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index 97b27bf9f2c..c281c5bcdbf 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -523,8 +523,9 @@ namespace hal bool bottomEdge = row == mRootItem->getChildCount(); auto desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; - ActionPingroup* act = new ActionPingroup(PinAction::MoveGroup,(u32)getIdOfItem(droppedGroup)); + ActionPingroup* act = new ActionPingroup(PinAction::MoveGroup); act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); + act->setGroupOrderNo(getIdOfItem(droppedGroup)); act->setPinOrderNo(desiredIdx); bool ok = act->exec(); if(ok){ diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index d0c4f3ae54e..ccdfa68590e 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -7,6 +7,19 @@ namespace hal { + int pinGroupIndex(const Module* mod, const PinGroup* pgrp) + { + if (!mod || !pgrp) return -1; + int inx = 0; + for (const PinGroup* testgrp : mod->get_pin_groups()) + { + if (testgrp == pgrp) return inx; + ++inx; + } + return -1; + } + + QString PinAction::toString(PinAction::Type tp) { QMetaEnum me = QMetaEnum::fromType(); @@ -144,7 +157,8 @@ namespace hal ActionPingroup* undo = nullptr; - if (mPinActions.size()==1 && mPinActions.at(0) == PinAction::MovePin) + if (mPinActions.size()==1 && + (mPinActions.at(0) == PinAction::MovePin || mPinActions.at(0) == PinAction::RemovePin)) { auto* pinToMove = parentModule->get_pin_by_id(mPinIds.at(0)); if (!pinToMove) return false; @@ -153,27 +167,30 @@ namespace hal int currentIndex = pinToMove->get_group().second; mSourceGroupId = srcgrp->get_id(); undo = new ActionPingroup(pinToMove->get_id(), currentIndex, mSourceGroupId, QString::fromStdString(srcgrp->get_name())); - if (srcgrp->get_pins().size() == 1) mPinActions.append(PinAction::Delete); - if (mTargetGroupId) - { - auto* tgtgrp = parentModule->get_pin_group_by_id(mTargetGroupId); - if (!tgtgrp) - tgtgrp = parentModule->get_pin_group_by_name(mName.toStdString()); - if (!tgtgrp) - mTargetGroupId = 0; - } - else + if (srcgrp->get_pins().size() == 1) mPinActions.append(PinAction::DeleteGroup); + if (mPinActions.at(0) == PinAction::MovePin) { - auto* tgtgrp = parentModule->get_pin_group_by_name(mName.toStdString()); - if (tgtgrp) - mTargetGroupId = tgtgrp->get_id(); - } - if (!mTargetGroupId) - { - // target group does not exist, create it - if (mName.isEmpty()) - mName = QString::fromStdString(pinToMove->get_name()); - mPinActions.replace(0,PinAction::Create); // create will place pins in new group + if (mTargetGroupId) + { + auto* tgtgrp = parentModule->get_pin_group_by_id(mTargetGroupId); + if (!tgtgrp) + tgtgrp = parentModule->get_pin_group_by_name(mName.toStdString()); + if (!tgtgrp) + mTargetGroupId = 0; + } + else + { + auto* tgtgrp = parentModule->get_pin_group_by_name(mName.toStdString()); + if (tgtgrp) + mTargetGroupId = tgtgrp->get_id(); + } + if (!mTargetGroupId) + { + // target group does not exist, create it + if (mName.isEmpty()) + mName = QString::fromStdString(pinToMove->get_name()); + mPinActions.replace(0,PinAction::Create); // create will place pins in new group + } } } @@ -199,13 +216,17 @@ namespace hal if (pin) pgrp->assign_pin(pin).is_ok(); } } + if (mGroupOrderNo >= 0) + parentModule->move_pin_group(res.get(),mGroupOrderNo).is_ok(); if (!undo) - undo = new ActionPingroup(PinAction::Delete, mTargetGroupId); + undo = new ActionPingroup(PinAction::DeleteGroup, mTargetGroupId); break; } - case PinAction::Delete: + case PinAction::DeleteGroup: { auto* pgrp = parentModule->get_pin_group_by_id(mSourceGroupId); + if (!pgrp) return false; + int currentIndex = pinGroupIndex(parentModule,pgrp); QList pins; for (const auto& pin : pgrp->get_pins()) { @@ -215,13 +236,14 @@ namespace hal { undo = new ActionPingroup(PinAction::Create, pgrp->get_id(), QString::fromStdString(pgrp->get_name())); undo->setPinIds(pins); + undo->setGroupOrderNo(currentIndex); } break; } case PinAction::MovePin: { - auto* tgtgrp = parentModule->get_pin_group_by_id(mTargetGroupId); - auto* pinToMove = parentModule->get_pin_by_id(mPinIds.at(0)); + PinGroup* tgtgrp = parentModule->get_pin_group_by_id(mTargetGroupId); + ModulePin* pinToMove = parentModule->get_pin_by_id(mPinIds.at(0)); if (!tgtgrp || !pinToMove) return false; auto* srcgrp = pinToMove->get_group().first; @@ -232,6 +254,14 @@ namespace hal tgtgrp->move_pin(pinToMove,mPinOrderNo).is_ok(); break; } + case PinAction::RemovePin: + { + PinGroup* srcgrp = parentModule->get_pin_group_by_id(mSourceGroupId); + ModulePin* pinToRemove = parentModule->get_pin_by_id(mPinIds.at(0)); + if (!srcgrp || !pinToRemove) return false; + srcgrp->remove_pin(pinToRemove).is_ok(); + break; + } case PinAction::MoveGroup: { PinGroup* srcgrp = nullptr; @@ -253,27 +283,51 @@ namespace hal undo->setPinOrderNo(currentIndex); } } - case PinAction::Rename: + case PinAction::RenamePin: + { + if (mPinIds.isEmpty()) return false; + auto* pin = parentModule->get_pin_by_id(mPinIds.first()); + if (!pin) return false; + QString oldName = QString::fromStdString(pin->get_name()); + pin->set_name(mName.toStdString()); + if (!undo) + { + undo = new ActionPingroup(PinAction::RenamePin, pin->get_id(), oldName); + } + } + case PinAction::RenameGroup: { auto* pgrp = parentModule->get_pin_group_by_id(mTargetGroupId); if (!pgrp) return false; QString oldName = QString::fromStdString(pgrp->get_name()); pgrp->set_name(mName.toStdString()); - if (undo) + if (!undo) { - undo->addPinAction(PinAction::Rename); - undo->setName(oldName); + undo = new ActionPingroup(PinAction::RenameGroup, pgrp->get_id(), oldName); } - else - undo = new ActionPingroup(PinAction::Rename, pgrp->get_id(), oldName); break; } case PinAction::TypeChange: + { + if (mPinIds.isEmpty()) return false; + auto* pin = parentModule->get_pin_by_id(mPinIds.first()); + if (!pin) return false; + PinType ptype = enum_from_string(mName.toStdString(),PinType::none); + PinType oldPtype = pin->get_type(); + pin->set_type(ptype); + if (!undo) + { + undo = new ActionPingroup(PinAction::TypeChange, 0, QString::fromStdString(enum_to_string(oldPtype))); + undo->setPinId(pin->get_id()); + } break; + } default: break; } } + if (undo) + undo->setObject(object()); mUndoAction = undo; return UserAction::exec(); } From 8cb19359260e73111fb62b85ab98b4e1cd4813ca Mon Sep 17 00:00:00 2001 From: joern274 Date: Fri, 7 Jul 2023 21:15:02 +0200 Subject: [PATCH 04/66] Data added to pin_changed event --- .../netlist/event_system/event_handler.h | 2 +- .../netlist/gate_library/enums/pin_type.h | 18 ++++++- include/hal_core/netlist/module.h | 2 + .../gui/graph_widget/graph_context_manager.h | 3 +- .../include/gui/netlist_relay/netlist_relay.h | 3 +- .../graph_widget/graph_context_manager.cpp | 2 +- .../gui/src/netlist_relay/netlist_relay.cpp | 9 ++-- src/netlist/module.cpp | 54 +++++++++++++------ 8 files changed, 67 insertions(+), 26 deletions(-) diff --git a/include/hal_core/netlist/event_system/event_handler.h b/include/hal_core/netlist/event_system/event_handler.h index 06c6b13372c..58570fdd81d 100644 --- a/include/hal_core/netlist/event_system/event_handler.h +++ b/include/hal_core/netlist/event_system/event_handler.h @@ -114,7 +114,7 @@ namespace hal gates_remove_begin, ///< associated_data = number of gates to remove gates_remove_end, ///< associated_data = number of removed gates gate_removed, ///< associated_data = id of removed gate - pin_changed, ///< no associated_data + pin_changed, ///< associated_data = [4LSB: type of action] [28HSB: id of pin group or pin] }; }; diff --git a/include/hal_core/netlist/gate_library/enums/pin_type.h b/include/hal_core/netlist/gate_library/enums/pin_type.h index 295e7d0acbc..ec073a7768b 100644 --- a/include/hal_core/netlist/gate_library/enums/pin_type.h +++ b/include/hal_core/netlist/gate_library/enums/pin_type.h @@ -54,4 +54,20 @@ namespace hal template<> std::map EnumStrings::data; -} // namespace hal \ No newline at end of file + + enum class PinEvent + { + unknown, + GroupCreate, + GroupReorder, + GroupRename, + GroupTypeChange, + GroupPinAssign, + GroupDelete, + PinCreate, + PinReorder, + PinRename, + PinTypeChange, + PinDelete + }; +} // namespace hal diff --git a/include/hal_core/netlist/module.h b/include/hal_core/netlist/module.h index df6c1f52728..c2951c23d37 100644 --- a/include/hal_core/netlist/module.h +++ b/include/hal_core/netlist/module.h @@ -688,6 +688,8 @@ namespace hal bool has_external_destination; }; + static u32 pinevent_associated_data(PinEvent pev, u32 id); + NetConnectivity check_net_endpoints(const Net* net) const; Result check_net(Net* net, bool recursive = false); Result assign_pin_net(const u32 pin_id, Net* net, PinDirection direction, const std::string& name = "", PinType type = PinType::none); diff --git a/plugins/gui/include/gui/graph_widget/graph_context_manager.h b/plugins/gui/include/gui/graph_widget/graph_context_manager.h index 55634c4aedb..46ef593ad04 100644 --- a/plugins/gui/include/gui/graph_widget/graph_context_manager.h +++ b/plugins/gui/include/gui/graph_widget/graph_context_manager.h @@ -26,6 +26,7 @@ #pragma once #include "hal_core/defines.h" +#include "hal_core/netlist/gate_library/enums/pin_type.h" #include #include @@ -193,7 +194,7 @@ namespace hal * * @param m - The module with the changed port */ - void handleModulePortsChanged(Module* m); + void handleModulePortsChanged(Module* m, hal::PinEvent pev, u32 pgid); /** * Handler to be called after a gate has been removed.
diff --git a/plugins/gui/include/gui/netlist_relay/netlist_relay.h b/plugins/gui/include/gui/netlist_relay/netlist_relay.h index 03a8bd3fb1b..6733a89cc50 100644 --- a/plugins/gui/include/gui/netlist_relay/netlist_relay.h +++ b/plugins/gui/include/gui/netlist_relay/netlist_relay.h @@ -26,6 +26,7 @@ #pragma once #include "hal_core/netlist/event_system/event_handler.h" +#include "hal_core/netlist/gate_library/enums/pin_type.h" #include "gui/grouping/grouping_color_serializer.h" #include #include @@ -332,7 +333,7 @@ namespace hal * @param m - The module with the changed port * @param respective_net - The id of the net of the renamed input port */ - void modulePortsChanged(Module* m) const; + void modulePortsChanged(Module* m, PinEvent pev, u32 pgid) const; /** * Q_SIGNAL to notify that the type of a module has been changed.
diff --git a/plugins/gui/src/graph_widget/graph_context_manager.cpp b/plugins/gui/src/graph_widget/graph_context_manager.cpp index e050e5b9452..04d21ad52df 100644 --- a/plugins/gui/src/graph_widget/graph_context_manager.cpp +++ b/plugins/gui/src/graph_widget/graph_context_manager.cpp @@ -348,7 +348,7 @@ namespace hal xout << "-------\n"; } - void GraphContextManager::handleModulePortsChanged(Module* m) + void GraphContextManager::handleModulePortsChanged(Module* m, PinEvent pev, u32 pgid) { for (GraphContext* context : mContextTableModel->list()) if (context->modules().contains(m->get_id())) diff --git a/plugins/gui/src/netlist_relay/netlist_relay.cpp b/plugins/gui/src/netlist_relay/netlist_relay.cpp index 92655c372ae..73448a0c97c 100644 --- a/plugins/gui/src/netlist_relay/netlist_relay.cpp +++ b/plugins/gui/src/netlist_relay/netlist_relay.cpp @@ -448,11 +448,12 @@ namespace hal break; } case ModuleEvent::event::pin_changed: { - //< no associated_data - - gGraphContextManager->handleModulePortsChanged(mod); + //< associated_data = [4LSB: type of action] [28HSB: id of pin group or pin] + PinEvent pev = (PinEvent) (associated_data&0xF); + u32 id = (associated_data >> 4); + gGraphContextManager->handleModulePortsChanged(mod,pev,id); - Q_EMIT modulePortsChanged(mod); + Q_EMIT modulePortsChanged(mod,pev,id); break; } case ModuleEvent::event::type_changed: { diff --git a/src/netlist/module.cpp b/src/netlist/module.cpp index 9b71ca00efd..6db8cc112e9 100644 --- a/src/netlist/module.cpp +++ b/src/netlist/module.cpp @@ -660,13 +660,13 @@ namespace hal { m_output_nets.insert(net); pin->set_direction(PinDirection::inout); - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); } else if (direction == PinDirection::output) { m_input_nets.insert(net); pin->set_direction(PinDirection::inout); - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); } } else @@ -687,7 +687,7 @@ namespace hal m_input_nets.insert(net); m_output_nets.erase(net); pin->set_direction(PinDirection::input); - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); } } else @@ -709,7 +709,7 @@ namespace hal m_output_nets.insert(net); m_input_nets.erase(net); pin->set_direction(PinDirection::output); - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); } } else @@ -831,6 +831,7 @@ namespace hal } else { + // create_pin_internal OK if (create_group) { if (const auto group_res = create_pin_group_internal(get_unique_pin_group_id(), name, direction, type, false, 0); group_res.is_error()) @@ -840,6 +841,7 @@ namespace hal } else { + // create_pin_group_internal OK if (const auto assign_res = group_res.get()->assign_pin(pin_res.get()); assign_res.is_error()) { assert(delete_pin_internal(pin_res.get()).is_ok()); @@ -847,9 +849,17 @@ namespace hal return ERR_APPEND(assign_res.get_error(), "could not create pin '" + name + "' for module '" + m_name + "' with ID " + std::to_string(m_id) + ": failed to assign pin to pin group"); } + else + { + // pin assigned to new group OK + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupCreate,group_res.get()->get_id())); + } } } - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + else + { + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinCreate,pin_res.get()->get_id())); + } return pin_res; } } @@ -1093,7 +1103,7 @@ namespace hal m_pin_names_map.erase(old_name); pin->set_name(new_name); m_pin_names_map[new_name] = pin; - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinRename,pin->get_id())); } return true; @@ -1116,7 +1126,7 @@ namespace hal if (pin->get_type() != new_type) { pin->set_type(new_type); - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); } return true; @@ -1161,7 +1171,7 @@ namespace hal m_pin_group_names_map.erase(old_name); pin_group->set_name(new_name); m_pin_group_names_map[new_name] = pin_group; - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupRename,pin_group->get_id())); } return true; @@ -1185,7 +1195,7 @@ namespace hal if (pin_group->get_type() != new_type) { pin_group->set_type(new_type); - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupTypeChange,pin_group->get_id())); } return true; } @@ -1212,7 +1222,7 @@ namespace hal if (pin_group->get_direction() != new_direction) { pin_group->set_direction(new_direction); - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupTypeChange,pin_group->get_id())); } return true; } @@ -1260,7 +1270,7 @@ namespace hal } } - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupCreate,pin_group->get_id())); return OK(pin_group); } @@ -1295,6 +1305,8 @@ namespace hal } } + u32 pin_group_id_to_delete = pin_group->get_id(); + if (auto res = delete_pin_group_internal(pin_group); res.is_error()) { return ERR(res.get_error()); @@ -1302,7 +1314,7 @@ namespace hal if (removed_pins) { - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupDelete,pin_group_id_to_delete)); } return OK({}); } @@ -1343,7 +1355,7 @@ namespace hal m_pin_groups_ordered.splice(dst_it, m_pin_groups_ordered, src_it); } - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupReorder,pin_group->get_id())); return OK({}); } @@ -1402,7 +1414,7 @@ namespace hal + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id)); } - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupPinAssign,pin_group->get_id())); return OK({}); } @@ -1438,7 +1450,7 @@ namespace hal + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id)); } - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinReorder,pin->get_id())); return OK({}); } @@ -1533,7 +1545,7 @@ namespace hal } } - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupCreate,pin->get_group().first->get_id())); return OK(pin); } @@ -1565,6 +1577,8 @@ namespace hal } } + u32 pin_id_to_delete = pin->get_id(); + if (const auto res = delete_pin_internal(pin); res.is_error()) { return ERR_APPEND(res.get_error(), @@ -1572,7 +1586,7 @@ namespace hal + ": failed to delete pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id())); } - m_event_handler->notify(ModuleEvent::event::pin_changed, this); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinDelete,pin_id_to_delete)); return OK({}); } @@ -1705,4 +1719,10 @@ namespace hal return OK({}); } + + u32 Module::pinevent_associated_data(PinEvent pev, u32 id) + { + return (id << 4) | (((u32)pev)&0xF); + } + } // namespace hal From 527138cbf498f47afcbf96fe37820d681a8f4ea7 Mon Sep 17 00:00:00 2001 From: joern274 Date: Thu, 13 Jul 2023 14:47:36 +0200 Subject: [PATCH 05/66] Fix: execute module pin method, avoid direct access to pin(group) method --- plugins/gui/src/user_action/action_pingroup.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index ccdfa68590e..12bb68034e9 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -232,6 +232,7 @@ namespace hal { pins.append(pin->get_id()); } + if (parentModule->delete_pin_group(pgrp).is_error()) return false; if (!undo) { undo = new ActionPingroup(PinAction::Create, pgrp->get_id(), QString::fromStdString(pgrp->get_name())); @@ -289,7 +290,7 @@ namespace hal auto* pin = parentModule->get_pin_by_id(mPinIds.first()); if (!pin) return false; QString oldName = QString::fromStdString(pin->get_name()); - pin->set_name(mName.toStdString()); + if (!parentModule->set_pin_name(pin,mName.toStdString())) return false; // RenamePin if (!undo) { undo = new ActionPingroup(PinAction::RenamePin, pin->get_id(), oldName); @@ -300,7 +301,7 @@ namespace hal auto* pgrp = parentModule->get_pin_group_by_id(mTargetGroupId); if (!pgrp) return false; QString oldName = QString::fromStdString(pgrp->get_name()); - pgrp->set_name(mName.toStdString()); + if (!parentModule->set_pin_group_name(pgrp,mName.toStdString())) return false; // RenameGroup if (!undo) { undo = new ActionPingroup(PinAction::RenameGroup, pgrp->get_id(), oldName); @@ -314,7 +315,7 @@ namespace hal if (!pin) return false; PinType ptype = enum_from_string(mName.toStdString(),PinType::none); PinType oldPtype = pin->get_type(); - pin->set_type(ptype); + if (!parentModule->set_pin_type(pin,ptype)) return false; // TypeChange if (!undo) { undo = new ActionPingroup(PinAction::TypeChange, 0, QString::fromStdString(enum_to_string(oldPtype))); From 9c94b1194beb2b6efbf856ea26bcac169e75ad33 Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 17 Jul 2023 09:54:13 +0200 Subject: [PATCH 06/66] add parameter to handleModulePortsChanged --- plugins/gui/include/gui/graph_widget/graph_context_manager.h | 2 +- .../module_details_widget/port_tree_model.h | 3 ++- plugins/gui/src/graph_widget/graph_context_manager.cpp | 2 ++ .../module_details_widget/port_tree_model.cpp | 4 +++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/gui/include/gui/graph_widget/graph_context_manager.h b/plugins/gui/include/gui/graph_widget/graph_context_manager.h index 46ef593ad04..82bf325352d 100644 --- a/plugins/gui/include/gui/graph_widget/graph_context_manager.h +++ b/plugins/gui/include/gui/graph_widget/graph_context_manager.h @@ -194,7 +194,7 @@ namespace hal * * @param m - The module with the changed port */ - void handleModulePortsChanged(Module* m, hal::PinEvent pev, u32 pgid); + void handleModulePortsChanged(Module* m, PinEvent pev, u32 pgid); /** * Handler to be called after a gate has been removed.
diff --git a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h index 349bc1e0422..d8dc53dc206 100644 --- a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h @@ -27,6 +27,7 @@ //#include "gui/new_selection_details_widget/models/base_tree_model.h" #include "gui/basic_tree_model/base_tree_model.h" +#include "hal_core/netlist/gate_library/enums/pin_type.h" #include namespace hal @@ -117,7 +118,7 @@ namespace hal /** @name Event Handler Functions */ ///@{ - void handleModulePortsChanged(Module* m); + void handleModulePortsChanged(Module* m, PinEvent pev, u32 pgid); ///@} //column identifier diff --git a/plugins/gui/src/graph_widget/graph_context_manager.cpp b/plugins/gui/src/graph_widget/graph_context_manager.cpp index 04d21ad52df..08cab39c589 100644 --- a/plugins/gui/src/graph_widget/graph_context_manager.cpp +++ b/plugins/gui/src/graph_widget/graph_context_manager.cpp @@ -350,6 +350,8 @@ namespace hal void GraphContextManager::handleModulePortsChanged(Module* m, PinEvent pev, u32 pgid) { + Q_UNUSED(pev); + Q_UNUSED(pgid); for (GraphContext* context : mContextTableModel->list()) if (context->modules().contains(m->get_id())) { diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index c281c5bcdbf..fe52aefa594 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -469,8 +469,10 @@ namespace hal return item->getAdditionalData(keyId).toInt(); } - void ModulePinsTreeModel::handleModulePortsChanged(Module* m) + void ModulePinsTreeModel::handleModulePortsChanged(Module* m, PinEvent pev, u32 pgid) { + Q_UNUSED(pev); + Q_UNUSED(pgid); if ((int)m->get_id() == mModuleId) { if (!mIgnoreEventsFlag) From 7a95b05a6e2f9c6bee58d5f4f9c5a6c8159a7507 Mon Sep 17 00:00:00 2001 From: joern274 Date: Sun, 23 Jul 2023 12:11:56 +0200 Subject: [PATCH 07/66] action_pingroup re-organized (work in progress, does not build) --- .../include/gui/user_action/action_pingroup.h | 110 +++++---- .../gui/src/user_action/action_pingroup.cpp | 215 +++++++++++------- 2 files changed, 207 insertions(+), 118 deletions(-) diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index 9a1e27e8c84..5a79b8b382e 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -31,59 +31,91 @@ namespace hal { - - class PinAction : public QObject + class PinActionType : public QObject { Q_OBJECT public: - enum Type { None, Create, DeleteGroup, RemovePin, MovePin, MoveGroup, RenamePin, RenameGroup, TypeChange, MaxAction }; - Q_ENUM(Type) + enum Type { None, GroupCreate, GroupDelete, GroupMove, GroupRename, GroupTypechange, GroupDirection, + PinAddgroup, PinRename, PinTypechange, PinDirection, PinSetindex, MaxAction }; + Q_ENUM(Type); + public: static QString toString(Type tp); static Type fromString(const QString& s); + static bool useExistingGroup(Type tp); + static bool useExistingPin(Type tp); }; int pinGroupIndex(const Module* mod, const PinGroup* pgrp); + /** * @ingroup user_action * @brief Pingroup user actions * - * Action depends on PinAction::Type: + * Arguments depends on PinActionType::Type: + * + * GroupCreate: + * ID : ID of group to create + * negative ID: call constructor without ID, however, + * ID will be used internally for subsequent commands related to crated group + * name : name of group + * value : start index, assume ascending + * negative value: descending order starting with (-value) + * + * GroupDelete + * ID : ID of group to delete + * + * GroupMove + * ID : ID of group to move + * value : new position in vector of pin groups * - * Create: - * Pingroup with given name gets created. - * Pins listed in pinIds get moved into new group - * Id of created group returned as targetGroupId() + * GroupRename + * ID : ID of group to rename + * name : new name * - * DeleteGroup: - * Pingroup ID=sourceGroupId gets deleted. - * Pins in group are stored for undo command + * GroupTypechange + * ID : ID of group to modifiy + * value : (int) PinType as of hal_core/netlist/gate_library/enums/pin_type.h * - * RemovePin: - * Remove pin from sourceGroup + * GroupDirection + * ID : ID of group to modifiy + * value : (int) PinDirection as of hal_core/netlist/gate_library/enums/pin_direction.h * - * MovePin: - * Must use move constructor with mandatory arguments pinId and pinOrderNo - * One out of targetIndex (existing pingroup) or name (create new pingroup) - * must be given to indicate destination + * PinAddgroup + * ID : ID of pin + * value : ID of group, might be negative if group recently created * - * MoveGroup: - * Move Group identified by sourceGroupId to position groupOrderNo - * RenamePin: - * Set new name to first pin in pinIds - * RenameGroup: - * Set new name to group identified by targetGroupId + * PinRename + * ID : ID of pin to rename + * name : new name + * + * PinTypechange + * ID : ID of pin to modify + * value : (int) PinType + * + * PinDirection + * ID : ID of pin to modify + * value : (int) PinDirection + * + * PinSetindex + * ID : ID of pin + * value : new index in pingroup. Calculated from row by + * index = startindex + a * row with a=1 for ascending, a=-1 for descending */ class ActionPingroup : public UserAction { + class AtomicAction + { + public: + PinActionType::Type mType; + int mId; + QString mName; + int mValue; + AtomicAction(PinActionType::Type tp, int id, const QString& name = QString(), int v=0) : mType(tp), mId(id), mName(name), mValue(v) {;} + }; + private: - QList mPinActions; - QList mPinIds; - u32 mSourceGroupId; - u32 mTargetGroupId; - int mPinOrderNo; - int mGroupOrderNo; - QString mName; + QList mPinActions; public: /** * Action Constructor. @@ -91,22 +123,16 @@ namespace hal * @param type - The UserActionObjectType of the item that should be created (default type: None) * @param objName - The name of the object to create (default name: ""). */ - ActionPingroup(PinAction::Type action = PinAction::None, u32 pingroupId = 0, const QString& name=QString()); - ActionPingroup(u32 pinId, int pinIndex, u32 tgtgroupId=0, const QString& name=QString(), int grpIndex=-1); // action = MovePin + ActionPingroup(PinActionType::Type tp = PinActionType::None, u32 id = 0, const QString& name=QString(), int value=0); bool exec() override; QString tagname() const override; void writeToXml(QXmlStreamWriter& xmlOut) const override; void readFromXml(QXmlStreamReader& xmlIn) override; void addToHash(QCryptographicHash& cryptoHash) const override; - void setPinIds(const QList& ids) { mPinIds = ids; } - void setPinId(u32 id) { mPinIds.clear(); mPinIds.append(id); } - void addPinAction(PinAction::Type action) { mPinActions.prepend(action); } - void setSourceGroupId(u32 id) { mSourceGroupId = id; } - void setTargetGroupId(u32 id) { mTargetGroupId = id; } - void setPinOrderNo(int inx) { mPinOrderNo = inx; } - void setGroupOrderNo(int inx) { mGroupOrderNo = inx; } - void setName(const QString& name) { mName = name; } - u32 targetGroupId() const { return mTargetGroupId; } + + static ActionPingroup* addPinsToExistingGroup(u32 grpId, QList pinIds); + static ActionPingroup* addPinsToNewGroup(const QString& name, QList pinIds); + static ActionPingroup* addPinToNewGroup(const QString& name, u32 pinId); }; /** diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index 12bb68034e9..0cf9a00af32 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -20,13 +20,13 @@ namespace hal } - QString PinAction::toString(PinAction::Type tp) + QString PinActionType::toString(PinActionType::Type tp) { QMetaEnum me = QMetaEnum::fromType(); return QString(me.key(tp)); } - PinAction::Type PinAction::fromString(const QString &s) + PinActionType::Type PinActionType::fromString(const QString &s) { QMetaEnum me = QMetaEnum::fromType(); for (int t = None; t < MaxAction; t++) @@ -37,6 +37,18 @@ namespace hal return None; } + bool PinActionType::useExistingGroup(PinActionType::Type tp) + { + static const QSet types = {GroupDelete, GroupMove, GroupRename, GroupTypechange, GroupDirection}; + return types.contains(tp); + } + + bool PinActionType::useExistingPin(PinActionType::Type tp) + { + static const QSet types = {PinAddgroup, PinRename, PinTypechange, PinDirection, PinSetindex}; + return types.contains(tp); + } + ActionPingroupFactory::ActionPingroupFactory() : UserActionFactory("Pingroup") {;} @@ -52,97 +64,57 @@ namespace hal return ActionPingroupFactory::sFactory->tagname(); } - ActionPingroup::ActionPingroup(PinAction::Type action, u32 pingroupId, const QString &name) - : mSourceGroupId(pingroupId), mTargetGroupId(pingroupId), mPinOrderNo(-1), mGroupOrderNo(-1), mName(name) + ActionPingroup::ActionPingroup(PinActionType::Type tp, u32 id, const QString &name, int value) { - switch (action) - { - case PinAction::None: - break; - case PinAction::MovePin: - // use move constructor - break; - default: - mPinActions.append(action); - } - } - - ActionPingroup::ActionPingroup(u32 pinId, int pinIndex, u32 tgtgroupId, const QString& name, int grpIndex) - : mSourceGroupId(0), mTargetGroupId(tgtgroupId), mPinOrderNo(pinIndex), mGroupOrderNo(grpIndex), mName(name) - { - mPinIds.append(pinId); - mPinActions.append(PinAction::MovePin); + mPinActions.append(AtomicAction(tp, id, name, value)); } void ActionPingroup::addToHash(QCryptographicHash &cryptoHash) const { - for (PinAction::Type tp : mPinActions) - cryptoHash.addData((char*)(&tp),sizeof(tp)); - for (u32 pid: mPinIds) - cryptoHash.addData((char*)(&pid),sizeof(pid)); - cryptoHash.addData((char*)(&mSourceGroupId),sizeof(mSourceGroupId)); - cryptoHash.addData((char*)(&mTargetGroupId),sizeof(mTargetGroupId)); - cryptoHash.addData((char*)(&mPinOrderNo),sizeof(mPinOrderNo)); - cryptoHash.addData((char*)(&mGroupOrderNo),sizeof(mGroupOrderNo)); + for (const AtomicAction& aa : mPinActions) + { + cryptoHash.addData((char*)(&aa.mType),sizeof(aa.mType)); + cryptoHash.addData((char*)(&aa.mId),sizeof(aa.mId)); + cryptoHash.addData(aa.mName.toUtf8()); + cryptoHash.addData((char*)(&aa.mValue),sizeof(aa.mValue)); + } } void ActionPingroup::writeToXml(QXmlStreamWriter& xmlOut) const { - //todo: remove parentId, switch entirely to parentObject - if (!mPinActions.isEmpty()) - { - QString s; - for (PinAction::Type tp : mPinActions) - { - if (!s.isEmpty()) s += ','; - s += PinAction::toString(tp); - } - xmlOut.writeTextElement("pinactions", s); - } - if (!mPinIds.isEmpty()) + // TODO xml parent element + for (const AtomicAction& aa : mPinActions) { - QString s; - for (u32 pinId : mPinIds) - { - if (!s.isEmpty()) s += ','; - s += QString::number(pinId); - } - xmlOut.writeTextElement("pinids", s); + if (aa.mType != PinActionType::None) + xmlOut.writeTextElement("type", PinActionType::toString(aa.mType)); + if (aa.mId) + xmlOut.writeTextElement("id", QString::number(aa.mId)); + if (!aa.mName.isEmpty()) + xmlOut.writeTextElement("name", aa.mName); + if (aa.mValue) + xmlOut.writeTextElement("value", QString::number(aa.mValue)); } - if (mSourceGroupId) - xmlOut.writeTextElement("srcgroupid", QString::number(mSourceGroupId)); - if (mTargetGroupId) - xmlOut.writeTextElement("tgtgroupid", QString::number(mTargetGroupId)); - if (mPinOrderNo >= 0) - xmlOut.writeTextElement("pinorder", QString::number(mPinOrderNo)); - if (mGroupOrderNo >= 0) - xmlOut.writeTextElement("grporder", QString::number(mGroupOrderNo)); } void ActionPingroup::readFromXml(QXmlStreamReader& xmlIn) { + // TODO loop xml parent element while (xmlIn.readNextStartElement()) { - //todo: emove parentId, switch entirely to parentObject - readParentObjectFromXml(xmlIn); - if (xmlIn.name() == "pinactions") - { - for (QString pinact : xmlIn.readElementText().split(',')) - mPinActions.append(PinAction::fromString(pinact)); - } - if (xmlIn.name() == "pinids") - { - for (QString pinid : xmlIn.readElementText().split(',')) - mPinIds.append(pinid.toInt()); - } - if (xmlIn.name() == "srcgroupid") - mSourceGroupId = xmlIn.readElementText().toInt(); - if (xmlIn.name() == "tgtgroupid") - mTargetGroupId = xmlIn.readElementText().toInt(); - if (xmlIn.name() == "pinorder") - mPinOrderNo = xmlIn.readElementText().toInt(); - if (xmlIn.name() == "grporder") - mGroupOrderNo = xmlIn.readElementText().toInt(); + PinActionType::Type tp = PinActionType::None; + int id = 0; + QString name; + int val = 0; + + if (xmlIn.name() == "type") + tp = PinActionType::fromString(xmlIn.readElementText()); + if (xmlIn.name() == "id") + id = xmlIn.readElementText().toInt(); + if (xmlIn.name() == "name") + name = xmlIn.readElementText(); + if (xmlIn.name() == "value") + val = xmlIn.readElementText().toInt(); + mPinActions.append(AtomicAction(tp,id,name,val)); } } @@ -157,6 +129,96 @@ namespace hal ActionPingroup* undo = nullptr; + QHash*> pgroups; + + for (const AtomicAction& aa : mPinActions) + { + PinGroup* pgroup = nullptr; + ModulePin* pin = nullptr; + + if (PinActionType::useExistingGroup(aa.mType)) + { + auto it = pgroups.find(aa.mId); + if (it == pgroups.end()) + { + pgroup = parentModule->get_pin_group_by_id(aa.mId); + if (!pgroup) return false; + pgroups.insert(aa.mId,pgroup); + } + else + pgroup = it.value(); + } + + if (PinActionType::useExistingPin(aa.mType)) + { + pin = parentModule->get_pin_by_id(aa.mId); + } + + switch (aa.mType) + { + case PinActionType::GroupCreate: + { + int startIndex = aa.mValue; + bool ascending = true; + if (aa.mValue < 0) + { + ascending = false; + startIndex = -aa.mValue; + } + if (aa.mId > 0) + { + if (auto res = parentModule->create_pin_group(aa.mId, aa.mName.toStdString(), {}, PinDirection::none, PinType::none,ascending,startIndex); res.is_ok()) + pgroups.insert(aa.mId,res.get()); + else + return false; + } + else + { + if (auto res = parentModule->create_pin_group(aa.mName.toStdString(), {}, PinDirection::none, PinType::none,ascending,startIndex); res.is_ok()) + pgroups.insert(aa.mId,res.get()); + else + return false; + } + break; + } + case PinActionType::GroupDelete: + if (parentModule->delete_pin_group(pgroup).is_error()) + return false; + break; + case PinActionType::GroupMove: + if (parentModule->move_pin_group(pgroup,aa.mValue).is_error()) + return false; + break; + case PinActionType::GroupRename: + if (!parentModule->set_pin_group_name(pgroup,aa.mName.toStdString())) + return false; + break; + case PinActionType::GroupTypechange: + if (!parentModule->set_pin_group_type(pgroup, (PinType) aa.mValue)) + return false; + break; + case PinActionType::GroupDirection: + if (!parentModule->set_pin_group_direction(pgroup, (PinDirection) aa.mValue)) + return false; + break; + case PinActionType::PinAddgroup: + break; + case PinActionType::PinRename: + if (!parentModule->set_pin_name(pin, aa.mName.toStdString())) + return false; + break; + case PinActionType::PinTypechange: + if (!parentModule->set_pin_type(pin, (PinType) aa.mValue)) + return false; + break; + case PinActionType::PinSetindex: + break; + default: + break; + } + } + + /* if (mPinActions.size()==1 && (mPinActions.at(0) == PinAction::MovePin || mPinActions.at(0) == PinAction::RemovePin)) { @@ -327,6 +389,7 @@ namespace hal break; } } + */ if (undo) undo->setObject(object()); mUndoAction = undo; From d69c988e3dba63395cbe8b858e9e8d00db688ffc Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 24 Jul 2023 19:21:29 +0200 Subject: [PATCH 08/66] action_pingroup re-organized --- .../include/gui/user_action/action_pingroup.h | 47 +- .../module_ports_tree.cpp | 34 +- .../module_details_widget/port_tree_model.cpp | 40 +- .../gui/src/user_action/action_pingroup.cpp | 446 ++++++++++-------- 4 files changed, 317 insertions(+), 250 deletions(-) diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index 5a79b8b382e..26703e791e6 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -36,7 +36,7 @@ namespace hal Q_OBJECT public: enum Type { None, GroupCreate, GroupDelete, GroupMove, GroupRename, GroupTypechange, GroupDirection, - PinAddgroup, PinRename, PinTypechange, PinDirection, PinSetindex, MaxAction }; + PinAsignGroup, PinRename, PinTypechange, PinDirection, PinSetindex, MaxAction }; Q_ENUM(Type); public: @@ -81,7 +81,7 @@ namespace hal * ID : ID of group to modifiy * value : (int) PinDirection as of hal_core/netlist/gate_library/enums/pin_direction.h * - * PinAddgroup + * PinAsignGroup * ID : ID of pin * value : ID of group, might be negative if group recently created * @@ -104,6 +104,7 @@ namespace hal */ class ActionPingroup : public UserAction { + private: class AtomicAction { public: @@ -114,25 +115,45 @@ namespace hal AtomicAction(PinActionType::Type tp, int id, const QString& name = QString(), int v=0) : mType(tp), mId(id), mName(name), mValue(v) {;} }; - private: + class GroupRestore + { + public: + int mId; + QString mName; + int mRow; + int mStartIndex; + PinDirection mDirection; + PinType mType; + GroupRestore(Module* m, PinGroup* pgroup); + }; + + QHash*> mPinGroups; QList mPinActions; + Module* mParentModule; + QMap mGroupRestore; + QSet mPinsMoved; + QSet mGroupToRemove; + + PinGroup* getGroup(ModulePin* pin) const; + PinGroup* getGroup(int grpId) const; + void prepareUndoAction(); + void finalizeUndoAction(); + void addUndoAction(PinActionType::Type tp, int id = 0, const QString& name=QString(), int value=0); + static int pinGroupRow(Module* m, PinGroup* pgroup); public: - /** - * Action Constructor. - * - * @param type - The UserActionObjectType of the item that should be created (default type: None) - * @param objName - The name of the object to create (default name: ""). - */ - ActionPingroup(PinActionType::Type tp = PinActionType::None, u32 id = 0, const QString& name=QString(), int value=0); + ActionPingroup(PinActionType::Type tp = PinActionType::None, int id = 0, const QString& name=QString(), int value=0); + ActionPingroup(const QList& aaList); bool exec() override; QString tagname() const override; void writeToXml(QXmlStreamWriter& xmlOut) const override; void readFromXml(QXmlStreamReader& xmlIn) override; void addToHash(QCryptographicHash& cryptoHash) const override; - static ActionPingroup* addPinsToExistingGroup(u32 grpId, QList pinIds); - static ActionPingroup* addPinsToNewGroup(const QString& name, QList pinIds); - static ActionPingroup* addPinToNewGroup(const QString& name, u32 pinId); + static ActionPingroup* addPinsToExistingGroup(const Module* m, u32 grpId, QList pinIds, int irow = -1); + static ActionPingroup* addPinToExistingGroup(const Module* m, u32 grpId, u32 pinId, int irow = -1); + static ActionPingroup* addPinsToNewGroup(const Module* m, const QString& name, QList pinIds); + static ActionPingroup* addPinToNewGroup(const Module* m, const QString& name, u32 pinId); + static ActionPingroup* removePinsFromGroup(const Module* m, QList pinIds); }; /** diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp index 96475ac7079..b45ffc7e309 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp @@ -135,10 +135,8 @@ namespace hal return; pins.append(pin->get_id()); } - ActionPingroup* act = new ActionPingroup(PinAction::MovePin,pinGroup->get_id()); - act->setPinIds(pins); - act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); - act->exec(); + ActionPingroup* act = ActionPingroup::addPinsToExistingGroup(mod,pinGroup->get_id(),pins); + if (act) act->exec(); } }); } @@ -154,7 +152,7 @@ namespace hal auto* group = gNetlist->get_module_by_id(modId)->get_pin_group_by_id(itemId); if (group != nullptr) { - ActionPingroup* act = new ActionPingroup(PinAction::RenameGroup,itemId,ipd.textValue()); + ActionPingroup* act = new ActionPingroup(PinActionType::GroupRename,itemId,ipd.textValue()); act->setObject(UserActionObject(modId, UserActionObjectType::Module)); act->exec(); } @@ -164,7 +162,7 @@ namespace hal auto* pinGroup = mod->get_pin_group_by_id(itemId); if (pinGroup != nullptr) { - ActionPingroup* act = new ActionPingroup(PinAction::DeleteGroup,(u32)itemId); + ActionPingroup* act = new ActionPingroup(PinActionType::GroupDelete,(u32)itemId); act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); } @@ -192,8 +190,7 @@ namespace hal auto* pin = mod->get_pin_by_id(itemId); if (pin != nullptr) { - ActionPingroup* act = new ActionPingroup(PinAction::RenamePin,0,ipd.textValue()); - act->setPinId(pin->get_id()); + ActionPingroup* act = new ActionPingroup(PinActionType::PinRename,pin->get_id(),ipd.textValue()); act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); } @@ -213,8 +210,9 @@ namespace hal if (cbd.exec() == QDialog::Accepted) { - ActionPingroup* act = new ActionPingroup(PinAction::TypeChange,0,cbd.textValue()); - act->setPinId(pin->get_id()); + PinType ptype = enum_from_string(cbd.textValue().toStdString(),PinType::none); + + ActionPingroup* act = new ActionPingroup(PinActionType::PinTypechange,pin->get_id(),"",(int)ptype); act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); } @@ -232,15 +230,13 @@ namespace hal //can be both single(simple right-click, no real selection) and multi-selection if (sameGroup.first && mod->get_pin_group_by_id(sameGroup.second)->size() > 1) { - menu.addAction("Remove selection from group", [this, selectedPins, mod, sameGroup]() { + menu.addAction("Remove selection from group", [this, selectedPins, mod /*, sameGroup*/]() { QList pins; for (auto item : selectedPins) pins.append(mPortModel->getIdOfItem(item)); - ActionPingroup* act = new ActionPingroup(PinAction::RemovePin,mod->get_pin_group_by_id(sameGroup.second)->get_id()); - act->setPinIds(pins); - act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); - act->exec(); + ActionPingroup* act = ActionPingroup::removePinsFromGroup(mod, pins); + if (act) act->exec(); }); } @@ -276,7 +272,7 @@ namespace hal if (ipd.exec() == QDialog::Accepted && !ipd.textValue().isEmpty()) { QList pins; - auto mod = gNetlist->get_module_by_id(modId); + Module* mod = gNetlist->get_module_by_id(modId); for (auto item : selectedPins) { auto* pin = mod->get_pin_by_id(mPortModel->getIdOfItem(item)); @@ -285,10 +281,8 @@ namespace hal pins.append(pin->get_id()); } - ActionPingroup* act = new ActionPingroup(PinAction::Create,0,ipd.textValue()); - act->setPinIds(pins); - act->setObject(UserActionObject(modId, UserActionObjectType::Module)); - act->exec(); + ActionPingroup* act = ActionPingroup::addPinsToNewGroup(mod,ipd.textValue(),pins); + if (act) act->exec(); } }); } diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index fe52aefa594..8c61b7482ff 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -282,6 +282,7 @@ namespace hal bool ModulePinsTreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { + Q_UNUSED(column) Q_UNUSED(action) if(!data->formats().contains("pintreemodel/item")) return false; @@ -494,22 +495,9 @@ namespace hal if (pins.isEmpty()) return; // no pins to move auto tgtgroup = mModule->get_pin_group_by_id(getIdOfItem(onDroppedGroup)); - int ntgt = tgtgroup->size(); - if (pins.size()>1) - { - UserActionCompound* compAct = new UserActionCompound; - compAct->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); - compAct->setUseCreatedObject(); - for (u32 pinId : pins) - compAct->addAction(new ActionPingroup(pinId,ntgt++,tgtgroup->get_id())); - compAct->exec(); - } - else - { - ActionPingroup* act = new ActionPingroup(pins.at(0),ntgt,tgtgroup->get_id()); - act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); - act->exec(); - } + + ActionPingroup* act = ActionPingroup::addPinsToExistingGroup(mModule,tgtgroup->get_id(),pins); + if (act) act->exec(); // too keep the order, ActionAddItemsToObject cannot be executed with all pins, but a ComAction must be created // with many ActionAddItemsToObject that contain a single pin each -> set destroys order of pins in source pingroup @@ -525,10 +513,8 @@ namespace hal bool bottomEdge = row == mRootItem->getChildCount(); auto desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; - ActionPingroup* act = new ActionPingroup(PinAction::MoveGroup); + ActionPingroup* act = new ActionPingroup(PinActionType::GroupMove,getIdOfItem(droppedGroup),"",desiredIdx); act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); - act->setGroupOrderNo(getIdOfItem(droppedGroup)); - act->setPinOrderNo(desiredIdx); bool ok = act->exec(); if(ok){ removeItem(droppedGroup); @@ -542,9 +528,7 @@ namespace hal mIgnoreEventsFlag = true; u32 pinId = getIdOfItem(droppedPin); - int inx = onDroppedGroup->getChildCount(); - ActionPingroup* act = new ActionPingroup(pinId,inx, getIdOfItem(onDroppedGroup)); - act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); + ActionPingroup* act = new ActionPingroup(PinActionType::PinAsignGroup,pinId,"",getIdOfItem(onDroppedGroup)); act->exec(); auto oldParent = droppedPin->getParent(); removeItem(droppedPin); @@ -561,15 +545,20 @@ namespace hal { mIgnoreEventsFlag = true; int desiredIdx = row; + ActionPingroup* act = nullptr; if(droppedPin->getParent() == onDroppedParent) // same group { int ownRow = droppedPin->getOwnRow(); bool bottomEdge = row == onDroppedParent->getChildCount(); desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; // insert item here + act = new ActionPingroup(PinActionType::PinSetindex,getIdOfItem(droppedPin),"",desiredIdx); // TODO : start_index, descending + } + else + { + act = ActionPingroup::addPinToExistingGroup(mModule,getIdOfItem(onDroppedParent),getIdOfItem(droppedPin),desiredIdx); + if (!act) return; } - - ActionPingroup* act = new ActionPingroup(getIdOfItem(droppedPin),desiredIdx,getIdOfItem(onDroppedParent)); act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); act->exec(); auto oldParent = droppedPin->getParent(); @@ -598,7 +587,7 @@ namespace hal while (mModule->get_pin_group_by_name(groupName.toStdString())) // pin group name already exists groupName = QString("%1_%2").arg(baseName).arg(cnt++); - +/* ActionPingroup* actMovePin = new ActionPingroup(pinToMove->get_id(),0,0,groupName); actMovePin->setObject(UserActionObject(mModuleId, UserActionObjectType::Module)); bool ok = actMovePin->exec(); @@ -624,6 +613,7 @@ namespace hal removeItem(droppedPin); insertItem(droppedPin, pinGroupItem, 0); } + */ mIgnoreEventsFlag = false; } diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index 0cf9a00af32..86c711b1878 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -19,7 +19,6 @@ namespace hal return -1; } - QString PinActionType::toString(PinActionType::Type tp) { QMetaEnum me = QMetaEnum::fromType(); @@ -45,7 +44,7 @@ namespace hal bool PinActionType::useExistingPin(PinActionType::Type tp) { - static const QSet types = {PinAddgroup, PinRename, PinTypechange, PinDirection, PinSetindex}; + static const QSet types = {PinAsignGroup, PinRename, PinTypechange, PinDirection, PinSetindex}; return types.contains(tp); } @@ -64,11 +63,15 @@ namespace hal return ActionPingroupFactory::sFactory->tagname(); } - ActionPingroup::ActionPingroup(PinActionType::Type tp, u32 id, const QString &name, int value) + ActionPingroup::ActionPingroup(PinActionType::Type tp, int id, const QString &name, int value) { mPinActions.append(AtomicAction(tp, id, name, value)); } + ActionPingroup::ActionPingroup(const QList& aaList) + : mPinActions(aaList) + {;} + void ActionPingroup::addToHash(QCryptographicHash &cryptoHash) const { for (const AtomicAction& aa : mPinActions) @@ -117,19 +120,126 @@ namespace hal mPinActions.append(AtomicAction(tp,id,name,val)); } } + PinGroup* ActionPingroup::getGroup(ModulePin* pin) const + { + return pin->get_group().first; + } + + PinGroup* ActionPingroup::getGroup(int grpId) const + { + PinGroup* pgroup = mPinGroups.value(grpId); + if (pgroup) return pgroup; + if (!mParentModule || grpId<=0) return nullptr; + return mParentModule->get_pin_group_by_id(grpId); + } + + void ActionPingroup::addUndoAction(PinActionType::Type tp, int id, const QString& name, int value) + { + ActionPingroup* undo = nullptr; + if (mUndoAction) + { + undo = static_cast(mUndoAction); + undo->mPinActions.append(AtomicAction(tp,id,name,value)); + } + else + { + undo = new ActionPingroup(tp,id,name,value); + undo->setObject(object()); + } + mUndoAction = undo; + } + + + void ActionPingroup::prepareUndoAction() + { + QHash*,int> remainingPins; + for (const AtomicAction& aa : mPinActions) + { + if (aa.mType != PinActionType::PinAsignGroup) + continue; + ModulePin* pin = mParentModule->get_pin_by_id(aa.mId); + PinGroup* pgroup = pin->get_group().first; + if (remainingPins.contains(pgroup)) + remainingPins[pgroup]--; + else + remainingPins[pgroup] = pgroup->size()-1; + } + + // for groups that will be deleted after pin assign a create action is needed in undo + for (auto it = remainingPins.begin(); it != remainingPins.end(); ++it) + { + if (it.value() > 0) continue; + GroupRestore gr(mParentModule,it.key()); + mGroupRestore.insert(gr.mRow,gr); + } + } + + void ActionPingroup::finalizeUndoAction() + { + QList restoreActions; + for (auto it = mGroupRestore.begin(); it != mGroupRestore.end(); ++it) + { + const GroupRestore& gr = it.value(); + restoreActions.append(AtomicAction(PinActionType::GroupCreate,gr.mId,gr.mName,gr.mStartIndex)); + restoreActions.append(AtomicAction(PinActionType::GroupMove,gr.mId,"",gr.mRow)); + if (gr.mType != PinType::none) + restoreActions.append(AtomicAction(PinActionType::GroupTypechange,gr.mId,"",(int)gr.mType)); + if (gr.mDirection != PinDirection::none) + restoreActions.append(AtomicAction(PinActionType::GroupDirection,gr.mId,"",(int)gr.mDirection)); + } + if (!restoreActions.isEmpty()) + { + if (mUndoAction) + { + ActionPingroup* act = static_cast(mUndoAction); + restoreActions += act->mPinActions; + act->mPinActions = restoreActions; + } + else + { + mUndoAction = new ActionPingroup(restoreActions); + } + } + + for (u32 grpId : mGroupToRemove) + { + if (mUndoAction) + { + ActionPingroup* act = static_cast(mUndoAction); + act->mPinActions.append(AtomicAction(PinActionType::GroupDelete,grpId)); + } + else + mUndoAction = new ActionPingroup(PinActionType::GroupDelete,grpId); + } + + if (mUndoAction) mUndoAction->setObject(object()); + } + + int ActionPingroup::pinGroupRow(Module *m, PinGroup* pgroup) + { + int inx = 0; + for (PinGroup* testgroup : m->get_pin_groups()) + { + if (testgroup == pgroup) return inx; + ++inx; + } + return -1; + } bool ActionPingroup::exec() { + mPinGroups.clear(); + mGroupRestore.clear(); + mPinsMoved.clear(); + mGroupToRemove.clear(); if (mObject.type() != UserActionObjectType::Module) return false; - Module* parentModule = gNetlist->get_module_by_id(mObject.id()); - if (!parentModule) + mParentModule = gNetlist->get_module_by_id(mObject.id()); + if (!mParentModule) return false; - ActionPingroup* undo = nullptr; - - QHash*> pgroups; + prepareUndoAction(); // create pingroups in case we are going to delete some while assigning for (const AtomicAction& aa : mPinActions) { @@ -138,12 +248,12 @@ namespace hal if (PinActionType::useExistingGroup(aa.mType)) { - auto it = pgroups.find(aa.mId); - if (it == pgroups.end()) + auto it = mPinGroups.find(aa.mId); + if (it == mPinGroups.end()) { - pgroup = parentModule->get_pin_group_by_id(aa.mId); + pgroup = mParentModule->get_pin_group_by_id(aa.mId); if (!pgroup) return false; - pgroups.insert(aa.mId,pgroup); + mPinGroups.insert(aa.mId,pgroup); } else pgroup = it.value(); @@ -151,7 +261,7 @@ namespace hal if (PinActionType::useExistingPin(aa.mType)) { - pin = parentModule->get_pin_by_id(aa.mId); + pin = mParentModule->get_pin_by_id(aa.mId); } switch (aa.mType) @@ -167,232 +277,184 @@ namespace hal } if (aa.mId > 0) { - if (auto res = parentModule->create_pin_group(aa.mId, aa.mName.toStdString(), {}, PinDirection::none, PinType::none,ascending,startIndex); res.is_ok()) - pgroups.insert(aa.mId,res.get()); + if (auto res = mParentModule->create_pin_group(aa.mId, aa.mName.toStdString(), {}, PinDirection::none, PinType::none,ascending,startIndex); res.is_ok()) + pgroup = res.get(); else return false; } else { - if (auto res = parentModule->create_pin_group(aa.mName.toStdString(), {}, PinDirection::none, PinType::none,ascending,startIndex); res.is_ok()) - pgroups.insert(aa.mId,res.get()); + if (auto res = mParentModule->create_pin_group(aa.mName.toStdString(), {}, PinDirection::none, PinType::none,ascending,startIndex); res.is_ok()) + pgroup = res.get(); else return false; } + if (pgroup) + { + mPinGroups[aa.mId] = pgroup; + mGroupToRemove.insert(pgroup->get_id()); + } break; } case PinActionType::GroupDelete: - if (parentModule->delete_pin_group(pgroup).is_error()) + { + int v = pgroup->get_start_index(); + if (!pgroup->is_ascending()) v = -v; + u32 id = pgroup->get_id(); + int ptype = (int) pgroup->get_type(); + int pdir = (int) pgroup->get_direction(); + QString name = QString::fromStdString(pgroup->get_name()); + if (mParentModule->delete_pin_group(pgroup).is_error()) return false; + addUndoAction(PinActionType::GroupCreate,id,name,v); + addUndoAction(PinActionType::GroupTypechange,id,"",ptype); + addUndoAction(PinActionType::GroupDirection,id,"",pdir); break; + } case PinActionType::GroupMove: - if (parentModule->move_pin_group(pgroup,aa.mValue).is_error()) + { + int inx = pinGroupRow(mParentModule,pgroup); + if (inx < 0) return false; + addUndoAction(PinActionType::GroupMove,pgroup->get_id(),"",inx); + if (mParentModule->move_pin_group(pgroup,aa.mValue).is_error()) return false; break; + } case PinActionType::GroupRename: - if (!parentModule->set_pin_group_name(pgroup,aa.mName.toStdString())) + addUndoAction(PinActionType::GroupRename,pgroup->get_id(),QString::fromStdString(pgroup->get_name())); + if (!mParentModule->set_pin_group_name(pgroup,aa.mName.toStdString())) return false; break; case PinActionType::GroupTypechange: - if (!parentModule->set_pin_group_type(pgroup, (PinType) aa.mValue)) + addUndoAction(PinActionType::GroupTypechange,pgroup->get_id(),"",(int)pgroup->get_type()); + if (!mParentModule->set_pin_group_type(pgroup, (PinType) aa.mValue)) return false; break; case PinActionType::GroupDirection: - if (!parentModule->set_pin_group_direction(pgroup, (PinDirection) aa.mValue)) + addUndoAction(PinActionType::GroupDirection,pgroup->get_id(),"",(int)pgroup->get_direction()); + if (!mParentModule->set_pin_group_direction(pgroup, (PinDirection) aa.mValue)) return false; break; - case PinActionType::PinAddgroup: + case PinActionType::PinAsignGroup: + addUndoAction(PinActionType::PinAsignGroup,aa.mId,"",pin->get_group().first->get_id()); + addUndoAction(PinActionType::PinSetindex,aa.mId,"",pin->get_group().second); + mPinsMoved.insert(aa.mId); + pgroup = getGroup(aa.mValue); + if (!pgroup) return false; + if (mParentModule->assign_pin_to_group(pgroup,pin).is_error()) + return false; break; case PinActionType::PinRename: - if (!parentModule->set_pin_name(pin, aa.mName.toStdString())) + addUndoAction(PinActionType::PinRename,aa.mId, QString::fromStdString(pin->get_name())); + if (!mParentModule->set_pin_name(pin, aa.mName.toStdString())) return false; break; case PinActionType::PinTypechange: - if (!parentModule->set_pin_type(pin, (PinType) aa.mValue)) + addUndoAction(PinActionType::PinTypechange,aa.mId,"",(int)pin->get_type()); + if (!mParentModule->set_pin_type(pin, (PinType) aa.mValue)) return false; break; case PinActionType::PinSetindex: + if (!mPinsMoved.contains(aa.mId)) + addUndoAction(PinActionType::PinSetindex,aa.mId,"",pin->get_group().second); + pgroup = pin->get_group().first; + if (!mParentModule->move_pin_within_group(pgroup,pin,aa.mValue).is_ok()) + return false; break; default: break; } } - /* - if (mPinActions.size()==1 && - (mPinActions.at(0) == PinAction::MovePin || mPinActions.at(0) == PinAction::RemovePin)) + finalizeUndoAction(); + + return UserAction::exec(); + } + + ActionPingroup* ActionPingroup::addPinsToExistingGroup(const Module *m, u32 grpId, QList pinIds, int irow) + { + ActionPingroup* retval = nullptr; + for (u32 pinId : pinIds) { - auto* pinToMove = parentModule->get_pin_by_id(mPinIds.at(0)); - if (!pinToMove) return false; - auto* srcgrp = pinToMove->get_group().first; - if (!srcgrp) return false; - int currentIndex = pinToMove->get_group().second; - mSourceGroupId = srcgrp->get_id(); - undo = new ActionPingroup(pinToMove->get_id(), currentIndex, mSourceGroupId, QString::fromStdString(srcgrp->get_name())); - if (srcgrp->get_pins().size() == 1) mPinActions.append(PinAction::DeleteGroup); - if (mPinActions.at(0) == PinAction::MovePin) - { - if (mTargetGroupId) - { - auto* tgtgrp = parentModule->get_pin_group_by_id(mTargetGroupId); - if (!tgtgrp) - tgtgrp = parentModule->get_pin_group_by_name(mName.toStdString()); - if (!tgtgrp) - mTargetGroupId = 0; - } - else - { - auto* tgtgrp = parentModule->get_pin_group_by_name(mName.toStdString()); - if (tgtgrp) - mTargetGroupId = tgtgrp->get_id(); - } - if (!mTargetGroupId) - { - // target group does not exist, create it - if (mName.isEmpty()) - mName = QString::fromStdString(pinToMove->get_name()); - mPinActions.replace(0,PinAction::Create); // create will place pins in new group - } - } + if (retval) + retval->mPinActions.append(AtomicAction(PinActionType::PinAsignGroup,pinId,"",grpId)); + else + retval = new ActionPingroup(PinActionType::PinAsignGroup,pinId,"",grpId); + if (irow >= 0) + retval->mPinActions.append(AtomicAction(PinActionType::PinSetindex,pinId,"",irow++)); } + retval->setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); + return retval; + } + ActionPingroup* ActionPingroup::addPinToExistingGroup(const Module* m, u32 grpId, u32 pinId, int irow) + { + QList pinIds; + pinIds << pinId; + return addPinsToExistingGroup(m,grpId,pinIds,irow); + } - for (PinAction::Type act : mPinActions) + ActionPingroup* ActionPingroup::addPinsToNewGroup(const Module* m, const QString& name, QList pinIds) + { + static int vid = -9; + ActionPingroup* retval = new ActionPingroup(PinActionType::GroupCreate,vid,name); + if (!pinIds.isEmpty()) { - switch (act) - { - case PinAction::None: - break; - case PinAction::Create: - { - auto res = parentModule->create_pin_group(mName.toStdString()); - if(res.is_error()) - return false; - mTargetGroupId = res.get()->get_id(); - if (!mPinIds.isEmpty()) - { - auto* pgrp = parentModule->get_pin_group_by_id(mTargetGroupId); - for (u32 pinId : mPinIds) - { - auto pin = parentModule->get_pin_by_id(pinId); - if (pin) pgrp->assign_pin(pin).is_ok(); - } - } - if (mGroupOrderNo >= 0) - parentModule->move_pin_group(res.get(),mGroupOrderNo).is_ok(); - if (!undo) - undo = new ActionPingroup(PinAction::DeleteGroup, mTargetGroupId); - break; - } - case PinAction::DeleteGroup: + ModulePin* pin = m->get_pin_by_id(pinIds.first()); + if (pin) { - auto* pgrp = parentModule->get_pin_group_by_id(mSourceGroupId); - if (!pgrp) return false; - int currentIndex = pinGroupIndex(parentModule,pgrp); - QList pins; - for (const auto& pin : pgrp->get_pins()) - { - pins.append(pin->get_id()); - } - if (parentModule->delete_pin_group(pgrp).is_error()) return false; - if (!undo) - { - undo = new ActionPingroup(PinAction::Create, pgrp->get_id(), QString::fromStdString(pgrp->get_name())); - undo->setPinIds(pins); - undo->setGroupOrderNo(currentIndex); - } - break; + retval->mPinActions.append(AtomicAction(PinActionType::GroupDirection,vid,"",(int)pin->get_direction())); + retval->mPinActions.append(AtomicAction(PinActionType::GroupTypechange,vid,"",(int)pin->get_type())); } - case PinAction::MovePin: - { - PinGroup* tgtgrp = parentModule->get_pin_group_by_id(mTargetGroupId); - ModulePin* pinToMove = parentModule->get_pin_by_id(mPinIds.at(0)); - if (!tgtgrp || !pinToMove) return false; - auto* srcgrp = pinToMove->get_group().first; + } + for (u32 pinId : pinIds) + retval->mPinActions.append(AtomicAction(PinActionType::PinAsignGroup,pinId,"",vid)); + retval->setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); + return retval; + } - if (srcgrp != tgtgrp) - { - if (tgtgrp->assign_pin(pinToMove).is_ok()) return false; - } - tgtgrp->move_pin(pinToMove,mPinOrderNo).is_ok(); - break; - } - case PinAction::RemovePin: - { - PinGroup* srcgrp = parentModule->get_pin_group_by_id(mSourceGroupId); - ModulePin* pinToRemove = parentModule->get_pin_by_id(mPinIds.at(0)); - if (!srcgrp || !pinToRemove) return false; - srcgrp->remove_pin(pinToRemove).is_ok(); - break; - } - case PinAction::MoveGroup: - { - PinGroup* srcgrp = nullptr; - int currentIndex = 0; - for (PinGroup* pgrp : parentModule->get_pin_groups()) - { - if (pgrp->get_id() == mSourceGroupId) - { - srcgrp = pgrp; - break; - } - ++ currentIndex; - } - if (!srcgrp) return false; - if (parentModule->move_pin_group(srcgrp,mPinOrderNo).is_error()) return false; - if (!undo) - { - undo = new ActionPingroup(PinAction::MoveGroup, mSourceGroupId); - undo->setPinOrderNo(currentIndex); - } - } - case PinAction::RenamePin: - { - if (mPinIds.isEmpty()) return false; - auto* pin = parentModule->get_pin_by_id(mPinIds.first()); - if (!pin) return false; - QString oldName = QString::fromStdString(pin->get_name()); - if (!parentModule->set_pin_name(pin,mName.toStdString())) return false; // RenamePin - if (!undo) - { - undo = new ActionPingroup(PinAction::RenamePin, pin->get_id(), oldName); - } - } - case PinAction::RenameGroup: - { - auto* pgrp = parentModule->get_pin_group_by_id(mTargetGroupId); - if (!pgrp) return false; - QString oldName = QString::fromStdString(pgrp->get_name()); - if (!parentModule->set_pin_group_name(pgrp,mName.toStdString())) return false; // RenameGroup - if (!undo) - { - undo = new ActionPingroup(PinAction::RenameGroup, pgrp->get_id(), oldName); - } - break; - } - case PinAction::TypeChange: - { - if (mPinIds.isEmpty()) return false; - auto* pin = parentModule->get_pin_by_id(mPinIds.first()); - if (!pin) return false; - PinType ptype = enum_from_string(mName.toStdString(),PinType::none); - PinType oldPtype = pin->get_type(); - if (!parentModule->set_pin_type(pin,ptype)) return false; // TypeChange - if (!undo) - { - undo = new ActionPingroup(PinAction::TypeChange, 0, QString::fromStdString(enum_to_string(oldPtype))); - undo->setPinId(pin->get_id()); - } - break; - } - default: - break; - } + ActionPingroup* ActionPingroup::addPinToNewGroup(const Module *m, const QString& name, u32 pinId) + { + QList pinIds; + pinIds << pinId; + return addPinsToNewGroup(m,name,pinIds); + } + + ActionPingroup* ActionPingroup::removePinsFromGroup(const Module* m, QList pinIds) + { + ActionPingroup* retval = nullptr; + QSet existingGroupNames; + for (PinGroup* pgroup : m->get_pin_groups()) + existingGroupNames.insert(QString::fromStdString(pgroup->get_name())); + int vid = -1; + QString basename; + for (u32 pinId : pinIds) + { + ModulePin* pin = m->get_pin_by_id(pinId); + if (!pin) return nullptr; + int count = 2; + QString name = basename = QString::fromStdString(pin->get_name()); + while (existingGroupNames.contains(name)) + name = QString("%1_%2").arg(basename).arg(count++); + if (retval) + retval->mPinActions.append(AtomicAction(PinActionType::GroupCreate,vid,name)); + else + retval = new ActionPingroup(PinActionType::GroupCreate,vid,name); + retval->mPinActions.append(AtomicAction(PinActionType::PinAsignGroup,pinId,"",vid)); + --vid; } - */ - if (undo) - undo->setObject(object()); - mUndoAction = undo; - return UserAction::exec(); + retval->setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); + return retval; + } + + ActionPingroup::GroupRestore::GroupRestore(Module* m, PinGroup* pgroup) + : mId(pgroup->get_id()), + mName(QString::fromStdString(pgroup->get_name())), + mRow(pinGroupRow(m,pgroup)), + mStartIndex(pgroup->get_start_index()), + mDirection(pgroup->get_direction()), + mType(pgroup->get_type()) + { + if (!pgroup->is_ascending()) mStartIndex = -mStartIndex; } } From a21ff3d68f2d881703af8f3ed3c504faa4f12ceb Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 2 Aug 2023 15:34:16 +0200 Subject: [PATCH 09/66] replace 'AdditionalData' from BaseTreeItem by more specific data fields (work in progress, does not compile yet) --- .../gui/basic_tree_model/base_tree_item.h | 29 +---- .../gate_details_widget/gate_pin_tree.h | 6 +- .../gate_details_widget/pin_tree_model.h | 49 ++++---- .../module_details_widget/module_tree_model.h | 32 +++-- .../module_details_widget/port_tree_model.h | 26 ++-- .../src/basic_tree_model/base_tree_item.cpp | 10 -- .../gate_details_widget/gate_pin_tree.cpp | 33 ++--- .../gate_details_widget/pin_tree_model.cpp | 32 ++--- .../module_elements_tree.cpp | 24 ++-- .../module_ports_tree.cpp | 12 +- .../module_tree_model.cpp | 116 ++++++++---------- .../module_details_widget/port_tree_model.cpp | 15 ++- 12 files changed, 161 insertions(+), 223 deletions(-) diff --git a/plugins/gui/include/gui/basic_tree_model/base_tree_item.h b/plugins/gui/include/gui/basic_tree_model/base_tree_item.h index 68f5ea461a9..8e05c2c63b6 100644 --- a/plugins/gui/include/gui/basic_tree_model/base_tree_item.h +++ b/plugins/gui/include/gui/basic_tree_model/base_tree_item.h @@ -35,15 +35,10 @@ namespace hal /** * @brief (Future) Base class for all tree models related to the details widget. * - * This class functions as a generic data container for all tree models. For this - * purpose, it uses QVariants as its main type of storage for its columns. (Note: Perhaps add - * additional data in form of a list or map (split it from "normal" displayed column data) + * This class functions as a generic data container for all tree models. */ class BaseTreeItem { - // maybe add enum type for all possible scenarios? or use additional data with key to access type - // and handle type handling in model...e.g.: item->getAddData("type")(structure, more generalization,...) - private: /** * Copy constructor. Copies the item's data, not the parent/children. @@ -194,31 +189,9 @@ namespace hal */ virtual int getOwnRow(); - /** - * Stores additional data. Can be accessed by getAdditionalData. - * (For example, a menu or color) - * - * @param key - The key to store the data under. - * @param data - The actual data to store. - */ - virtual void setAdditionalData(QString key, QVariant data); - - /** - * Retrieve the data stored under the given key. - * - * @param key - The key for the requested data. - * @return The data if something was stored under the key, empty QVariant otherwise. - */ - virtual QVariant getAdditionalData(QString key) const; - private: BaseTreeItem* mParent; QList mChildren; - - // experimental, additional data (for anything) - QMap mAdditionalData; - //QList mAdditionalData; - }; /** diff --git a/plugins/gui/include/gui/selection_details_widget/gate_details_widget/gate_pin_tree.h b/plugins/gui/include/gui/selection_details_widget/gate_details_widget/gate_pin_tree.h index 1d23ff76df1..746eb58931e 100644 --- a/plugins/gui/include/gui/selection_details_widget/gate_details_widget/gate_pin_tree.h +++ b/plugins/gui/include/gui/selection_details_widget/gate_details_widget/gate_pin_tree.h @@ -33,7 +33,7 @@ namespace hal { class GatePinsTreeModel; class Gate; - class BaseTreeItem; + class PinTreeItem; class GraphNavigationWidget; /** @@ -98,8 +98,8 @@ namespace hal bool mClearSelection; //helper functions - void buildPythonMenuForPin(QMenu &menu, BaseTreeItem* clickedPinItem); - void buildPythonMenuForPinGroup(QMenu &menu, BaseTreeItem* clickedPinIGrouptem); + void buildPythonMenuForPin(QMenu &menu, PinTreeItem* clickedPinItem); + void buildPythonMenuForPinGroup(QMenu &menu, PinTreeItem* clickedPinIGrouptem); void addSourceOurDestinationToSelection(int netId, bool isInputPin); void handleNavigationCloseRequested(); void handleNavigationJumpRequested(const Node& origin, const u32 via_net, const QSet& to_gates, const QSet& to_modules); diff --git a/plugins/gui/include/gui/selection_details_widget/gate_details_widget/pin_tree_model.h b/plugins/gui/include/gui/selection_details_widget/gate_details_widget/pin_tree_model.h index 142601fa32a..9a938078830 100644 --- a/plugins/gui/include/gui/selection_details_widget/gate_details_widget/pin_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/gate_details_widget/pin_tree_model.h @@ -26,8 +26,10 @@ #pragma once //#include "gui/new_selection_details_widget/models/base_tree_model.h" +#include "hal_core/defines.h" #include "gui/basic_tree_model/base_tree_model.h" #include +#include namespace hal { @@ -36,8 +38,12 @@ namespace hal class PinTreeItem : public BaseTreeItem { + public: + enum Type {None, Pin, Group}; private: + Type mType; + QList mNetIds; std::string mPinName; QString mPinDirection; QString mPinType; @@ -51,6 +57,24 @@ namespace hal void setDataAtIndex(int index, QVariant& data) override; void appendData(QVariant data) override; int getColumnCount() const override; + void setType(Type tp) { mType = tp; } + + /** + * Get the type (enum) of a given item. + * + * @return The item's type. + */ + Type type() const { return mType; } + void setNetIds(const QList& nids) { mNetIds = nids; } + + /** + * Get the connected nets for a given treeitem (represents a pin). If the + * item is grouping type or the pin has no connected net, an empty list + * is returned. In case of an inout pin, even multiple connected nets are possible. + * + * @return A list of net ids. + */ + QList netIds() const { return mNetIds; } }; /** @@ -63,9 +87,6 @@ class GatePinsTreeModel : public BaseTreeModel public: - //metatype declaration at the end of file - enum class itemType {grouping = 0, pin = 1}; - /** * The constructor. * @@ -98,24 +119,6 @@ class GatePinsTreeModel : public BaseTreeModel */ int getCurrentGateID(); - /** - * Get the connected nets for a given treeitem (represents a pin). If the - * item is grouping type or the pin has no connected net, an empty list - * is returned. In case of an inout pin, even multiple connected nets are possible. - * - * @param item - The treeitem from which to get the connected nets. - * @return A list of net ids. - */ - QList getNetIDsOfTreeItem(BaseTreeItem* item); - - /** - * Get the type (enum) of a given item. - * - * @param item - The item for which the type is requested. - * @return The item's type. - */ - itemType getTypeOfItem(BaseTreeItem* item); - /** * Get the number of displayed pins (the number of pins of all types). * @@ -136,10 +139,8 @@ class GatePinsTreeModel : public BaseTreeModel private: int mGateId; - QMap mPinGroupingToTreeItem; + QMap mPinGroupToTreeItem; }; } - -Q_DECLARE_METATYPE(hal::GatePinsTreeModel::itemType) diff --git a/plugins/gui/include/gui/selection_details_widget/module_details_widget/module_tree_model.h b/plugins/gui/include/gui/selection_details_widget/module_details_widget/module_tree_model.h index bf2c7d7ee29..a8a139e1b24 100644 --- a/plugins/gui/include/gui/selection_details_widget/module_details_widget/module_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/module_details_widget/module_tree_model.h @@ -39,19 +39,30 @@ namespace hal class ModuleTreeitem : public BaseTreeItem { + public: + enum ItemType { None, Module, Gate}; private: - std::string mType; + ItemType mItemType; int mId; - std::string mName; + QString mName; + QString mNodeType; public: - ModuleTreeitem(const std::string& name, int id, std::string tp); + ModuleTreeitem(ItemType itp, int id, const QString& name, const QString& ntp); QVariant getData(int column) const override; void setData(QList data) override; void setDataAtIndex(int index, QVariant& data) override; void appendData(QVariant data) override; int getColumnCount() const override; + + /** + * Get the type (enum) of a given item. + * + * @param item - The item for which the type is requested. + * @return The item's type. + */ + ItemType itemType() const { return mItemType; } }; class ModuleTreeModel : public BaseTreeModel @@ -59,9 +70,6 @@ namespace hal Q_OBJECT public: - //metatype declaration at the end of file - enum class itemType {module = 0, gate = 1}; - ModuleTreeModel(QObject* parent = nullptr); ~ModuleTreeModel(); @@ -81,14 +89,6 @@ namespace hal QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; ///@} - /** - * Get the type (enum) of a given item. - * - * @param item - The item for which the type is requested. - * @return The item's type. - */ - itemType getTypeOfItem(BaseTreeItem* item) const; - /** * Disconnects all events from the model. Can be called to increase performance when * no module is displayed. @@ -142,7 +142,7 @@ namespace hal * @param item - The requested item. * @return A module, net, or gate icon depending on the item's type. */ - QIcon getIconFromItem(BaseTreeItem* item) const; + QIcon getIconFromItem(ModuleTreeitem* item) const; void clearOwnStructures(); @@ -165,5 +165,3 @@ namespace hal }; } - -Q_DECLARE_METATYPE(hal::ModuleTreeModel::itemType) diff --git a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h index e5fe4d1e6c0..17080bafb79 100644 --- a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h @@ -38,21 +38,29 @@ namespace hal class PortTreeItem : public BaseTreeItem { + public: + enum Type {None, Pin, Group}; private: + Type mType; + u32 mId; QString mPinName; QString mPinDirection; QString mPinType; QString mNetName; public: - PortTreeItem(QString pinName, QString pinDirection, QString pinType, QString netName); - PortTreeItem(); + PortTreeItem(Type tp, QString pinName, QString pinDirection, QString pinType, QString netName); + PortTreeItem() : mType(None), mId(0) {;} QVariant getData(int column) const override; void setData(QList data) override; void setDataAtIndex(int index, QVariant& data) override; void appendData(QVariant data) override; int getColumnCount() const override; + setType(Type tp) { mType = tp; } + Type type() const { return mType; } + void setId(u32 id_) { mId = id_; } + u32 id() const { return mId; } }; /** @@ -63,10 +71,6 @@ namespace hal Q_OBJECT public: - //metatype declaration at the end of file (portSingleBit and portMultiBit are deprecated) - //important now are pins and groups - enum class itemType{portSingleBit = 0, portMultiBit = 1, pin = 2, group = 3}; - /** * The constructor. * @@ -117,14 +121,6 @@ namespace hal */ int getRepresentedModuleId(); - /** - * Get the type (enum) of a given item. - * - * @param item - The item for which the type is requested. - * @return The item's type. - */ - itemType getTypeOfItem(PortTreeItem* item) const; - /** * Returns the pin-id if the item represents a pin or the pingroup-id * if the item represents a pingroup. @@ -179,5 +175,3 @@ namespace hal void dndPinBetweenGroup(PortTreeItem* droppedPin, int row); }; } - -Q_DECLARE_METATYPE(hal::ModulePinsTreeModel::itemType) diff --git a/plugins/gui/src/basic_tree_model/base_tree_item.cpp b/plugins/gui/src/basic_tree_model/base_tree_item.cpp index c0d0ce9e539..5bf967777ea 100644 --- a/plugins/gui/src/basic_tree_model/base_tree_item.cpp +++ b/plugins/gui/src/basic_tree_model/base_tree_item.cpp @@ -99,16 +99,6 @@ namespace hal return mParent->getRowForChild(this); } - void BaseTreeItem::setAdditionalData(QString key, QVariant data) - { - mAdditionalData.insert(key, data); - } - - QVariant BaseTreeItem::getAdditionalData(QString key) const - { - return mAdditionalData.value(key, QVariant()); - } - QVariant RootTreeItem::getData(int column) const { if (column <= mHeaderLabels.size()) diff --git a/plugins/gui/src/selection_details_widget/gate_details_widget/gate_pin_tree.cpp b/plugins/gui/src/selection_details_widget/gate_details_widget/gate_pin_tree.cpp index f1ed3a05928..754770955ef 100644 --- a/plugins/gui/src/selection_details_widget/gate_details_widget/gate_pin_tree.cpp +++ b/plugins/gui/src/selection_details_widget/gate_details_widget/gate_pin_tree.cpp @@ -69,11 +69,11 @@ namespace hal if(!idx.isValid()) return; - auto clickedItem = mPinModel->getItemFromIndex(idx); - if(mPinModel->getTypeOfItem(clickedItem) != GatePinsTreeModel::itemType::pin) + PinTreeItem* clickedItem = dynamic_cast(mPinModel->getItemFromIndex(idx)); + if(!clickedItem || clickedItem->type() != PinTreeItem::Pin) return; - auto netId = mPinModel->getNetIDsOfTreeItem(clickedItem).front(); + auto netId = clickedItem->netIds().front(); auto clickedNet = gNetlist->get_net_by_id(netId); if(clickedNet) { @@ -96,9 +96,8 @@ namespace hal if(!idx.isValid()) return; - BaseTreeItem* clickedItem = mPinModel->getItemFromIndex(idx); + PinTreeItem* clickedItem = dynamic_cast(mPinModel->getItemFromIndex(idx)); QMenu menu; - GatePinsTreeModel::itemType type = mPinModel->getTypeOfItem(clickedItem); bool isMiscSectionSet = false;//so that the misc-section is not set multiple times //PLAINTEXT: NAME, DIRECTION, TYPE @@ -118,9 +117,9 @@ namespace hal }); //Check if jump to source or destination is possible - if(type == GatePinsTreeModel::itemType::pin && mPinModel->getNetIDsOfTreeItem(clickedItem).size()==1) + if(clickedItem->type() == PinTreeItem::Pin && clickedItem->netIds().size()==1) { - auto netId = mPinModel->getNetIDsOfTreeItem(clickedItem).front(); + auto netId = clickedItem->netIds().front(); auto clickedNet = gNetlist->get_net_by_id(netId); if(clickedNet) { @@ -156,15 +155,19 @@ namespace hal } //Add nets to selection if possible - QList netIds; - if(type == GatePinsTreeModel::itemType::pin) + QList netIds; + if(clickedItem->type() == PinTreeItem::Pin) { - netIds = mPinModel->getNetIDsOfTreeItem(clickedItem); + netIds = clickedItem->netIds(); } else { for(auto childItem : clickedItem->getChildren()) - netIds.append(mPinModel->getNetIDsOfTreeItem(childItem)); + { + PinTreeItem* pti = dynamic_cast(childItem); + if (pti) + netIds.append(pti->netIds()); + } } if(netIds.size() != 0) { @@ -191,7 +194,7 @@ namespace hal menu.addSection("Python"); - if(type == GatePinsTreeModel::itemType::pin) + if(clickedItem->type() == PinTreeItem::Pin) buildPythonMenuForPin(menu, clickedItem); else buildPythonMenuForPinGroup(menu, clickedItem); @@ -201,10 +204,10 @@ namespace hal } - void GatePinTree::buildPythonMenuForPin(QMenu &menu, BaseTreeItem *clickedPinItem) + void GatePinTree::buildPythonMenuForPin(QMenu &menu, PinTreeItem *clickedPinItem) { // 1.) NET-OBJECT - QList netIdsOfItem = mPinModel->getNetIDsOfTreeItem(clickedPinItem); + QList netIdsOfItem = clickedPinItem->netIds(); QString pythonCommandNetIds, pythonCommandName; if(netIdsOfItem.size() == 1) @@ -243,7 +246,7 @@ namespace hal } - void GatePinTree::buildPythonMenuForPinGroup(QMenu &menu, BaseTreeItem *clickedPinIGrouptem) + void GatePinTree::buildPythonMenuForPinGroup(QMenu &menu, PinTreeItem *clickedPinIGrouptem) { // 1. PYTHON LIST OF PIN GROUPS QString pythonList = "["; diff --git a/plugins/gui/src/selection_details_widget/gate_details_widget/pin_tree_model.cpp b/plugins/gui/src/selection_details_widget/gate_details_widget/pin_tree_model.cpp index f9b7ea9703f..1890580ec9f 100644 --- a/plugins/gui/src/selection_details_widget/gate_details_widget/pin_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/gate_details_widget/pin_tree_model.cpp @@ -101,7 +101,7 @@ namespace hal void GatePinsTreeModel::clear() { BaseTreeModel::clear(); - mPinGroupingToTreeItem.clear(); + mPinGroupToTreeItem.clear(); mGateId = -1; } @@ -123,7 +123,7 @@ namespace hal //evaluate netname (in case of inout multiple possible nets), method depends on pindirection (kind of ugly switch) QString netName = ""; - QList netIDs; + QList netIDs; switch (direction) { case PinDirection::input: @@ -161,20 +161,20 @@ namespace hal } pinItem->setData(QList() << QString::fromStdString(pin->get_name()) << pinDirection << pinType << netName); - pinItem->setAdditionalData(keyType, QVariant::fromValue(itemType::pin)); - pinItem->setAdditionalData(keyRepresentedNetsID, QVariant::fromValue(netIDs)); + pinItem->setType(PinTreeItem::Pin); + pinItem->setNetIds(netIDs); if (!grouping.empty()) { - BaseTreeItem* groupingsItem = mPinGroupingToTreeItem.value(grouping, nullptr); //since its a map, its okay - if (!groupingsItem) + PinTreeItem* pingroupItem = dynamic_cast(mPinGroupToTreeItem.value(grouping, nullptr)); //since its a map, its okay + if (!pingroupItem) { //assume all items in the same grouping habe the same direction and type, so the grouping-item has also these types - groupingsItem = new PinTreeItem(grouping, pinDirection, pinType, ""); - groupingsItem->setAdditionalData(keyType, QVariant::fromValue(itemType::grouping)); - mRootItem->appendChild(groupingsItem); - mPinGroupingToTreeItem.insert(grouping, groupingsItem); + pingroupItem = new PinTreeItem(grouping, pinDirection, pinType, ""); + pingroupItem->setType(PinTreeItem::Group); + mRootItem->appendChild(pingroupItem); + mPinGroupToTreeItem.insert(grouping, pingroupItem); } - groupingsItem->appendChild(pinItem); + pingroupItem->appendChild(pinItem); } else mRootItem->appendChild(pinItem); @@ -187,16 +187,6 @@ namespace hal return mGateId; } - QList GatePinsTreeModel::getNetIDsOfTreeItem(BaseTreeItem* item) - { - return item->getAdditionalData(keyRepresentedNetsID).value>(); - } - - GatePinsTreeModel::itemType GatePinsTreeModel::getTypeOfItem(BaseTreeItem* item) - { - return item->getAdditionalData(keyType).value(); - } - int GatePinsTreeModel::getNumberOfDisplayedPins() { Gate* g = gNetlist->get_gate_by_id(mGateId); diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_elements_tree.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_elements_tree.cpp index 208a6c1d362..30e3cf47006 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_elements_tree.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_elements_tree.cpp @@ -61,9 +61,9 @@ namespace hal if(!clickedIndex.isValid()) return; - BaseTreeItem* clickedItem = mModel->getItemFromIndex(clickedIndex); + ModuleTreeitem* clickedItem = dynamic_cast(mModel->getItemFromIndex(clickedIndex)); int id = clickedItem->getData(ModuleTreeModel::sIdColumn).toInt(); - ModuleTreeModel::itemType type = mModel->getTypeOfItem(clickedItem); + ModuleTreeitem::ItemType type = clickedItem->itemType(); QMenu menu; //menu.addSection("here comes the plaintext"); @@ -97,8 +97,8 @@ namespace hal gSelectionRelay->clear(); switch(type) { - case ModuleTreeModel::itemType::module: gSelectionRelay->addModule(id); break; - case ModuleTreeModel::itemType::gate: gSelectionRelay->addGate(id); break; + case ModuleTreeitem::Module: gSelectionRelay->addModule(id); break; + case ModuleTreeitem::Gate: gSelectionRelay->addGate(id); break; } gSelectionRelay->relaySelectionChanged(this); } @@ -109,8 +109,8 @@ namespace hal { switch(type) { - case ModuleTreeModel::itemType::module: gSelectionRelay->addModule(id); break; - case ModuleTreeModel::itemType::gate: gSelectionRelay->addGate(id); break; + case ModuleTreeitem::Module: gSelectionRelay->addModule(id); break; + case ModuleTreeitem::Gate: gSelectionRelay->addGate(id); break; } gSelectionRelay->relaySelectionChanged(this); } @@ -122,8 +122,8 @@ namespace hal Node nd; switch(type) { - case ModuleTreeModel::itemType::module: nd = Node(id, Node::Module); break; - case ModuleTreeModel::itemType::gate: nd = Node(id, Node::Gate); break; + case ModuleTreeitem::Module: nd = Node(id, Node::Module); break; + case ModuleTreeitem::Gate: nd = Node(id, Node::Gate); break; } SelectionTreeView::isolateInNewViewAction(nd); } @@ -134,16 +134,16 @@ namespace hal { switch(type) { - case ModuleTreeModel::itemType::module: gContentManager->getGraphTabWidget()->handleModuleFocus(id); break; - case ModuleTreeModel::itemType::gate: gContentManager->getGraphTabWidget()->handleGateFocus(id); break; + case ModuleTreeitem::Module: gContentManager->getGraphTabWidget()->handleModuleFocus(id); break; + case ModuleTreeitem::Gate: gContentManager->getGraphTabWidget()->handleGateFocus(id); break; } } ); menu.addSection("Python Code"); - QString pythonGetObject = (type == ModuleTreeModel::itemType::module) ? PyCodeProvider::pyCodeModule(id) : PyCodeProvider::pyCodeGate(id); - QString pythonDescription = (type == ModuleTreeModel::itemType::module) ? "Get module" : "Get gate"; + QString pythonGetObject = (type == ModuleTreeitem::Module) ? PyCodeProvider::pyCodeModule(id) : PyCodeProvider::pyCodeGate(id); + QString pythonDescription = (type == ModuleTreeitem::Module) ? "Get module" : "Get gate"; menu.addAction(QIcon(":/icons/python"), pythonDescription, [pythonGetObject]() { diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp index 376f2005070..f2f4e988463 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp @@ -88,8 +88,7 @@ namespace hal return; //all relevant information - PortTreeItem* clickedItem = static_cast(mPortModel->getItemFromIndex(clickedIndex)); - ModulePinsTreeModel::itemType type = mPortModel->getTypeOfItem(clickedItem); + PortTreeItem* clickedItem = static_cast(mPortModel->getItemFromIndex(clickedIndex)); Net* n = mPortModel->getNetFromItem(clickedItem); QString name = clickedItem->getData(ModulePinsTreeModel::sNameColumn).toString(); u32 modId = mPortModel->getRepresentedModuleId(); @@ -141,7 +140,7 @@ namespace hal }); } - if (type == ModulePinsTreeModel::itemType::group) //group specific context, own helper function? (returns at the end) + if (clickedItem->type() == PortTreeItem::Group) //group specific context, own helper function? (returns at the end) { menu.addAction("Change name", [name, modId, itemId]() { InputDialog ipd("Change pin group name", "New group name", name); @@ -245,7 +244,7 @@ namespace hal appendMultiSelectionEntries(menu, modId); menu.addSection("Python"); - if(type == ModulePinsTreeModel::itemType::pin) + if(clickedItem->type()==PortTreeItem::Pin) menu.addAction(QIcon(":/icons/python"), "Get pin", [modId, itemId]() { QApplication::clipboard()->setText(PyCodeProvider::pyCodeModulePinById(modId, itemId)); }); else menu.addAction(QIcon(":/icons/python"), "Get group", [modId, itemId]() { QApplication::clipboard()->setText(PyCodeProvider::pyCodeModulePinGroup(modId, itemId)); }); @@ -298,8 +297,7 @@ namespace hal for (auto index : selectionModel()->selectedRows()) { PortTreeItem* item = static_cast(mPortModel->getItemFromIndex(index)); - auto itemType = mPortModel->getTypeOfItem(item); - if (itemType == ModulePinsTreeModel::itemType::pin) + if (item->type() == PortTreeItem::Pin) { if (!alreadyProcessedPins.contains(item)) { @@ -307,7 +305,7 @@ namespace hal alreadyProcessedPins.insert(item); } } - else if (itemType == ModulePinsTreeModel::itemType::group) + else if (item->type() == PortTreeItem::Group) { onlyPins = false; for (auto pinItem : item->getChildren()) diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_tree_model.cpp index d04b547c2f5..38f2e058d32 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_tree_model.cpp @@ -10,34 +10,28 @@ namespace hal { - ModuleTreeitem::ModuleTreeitem(const std::string &name, int id, std::string tp) - : mType(tp), mId(id), mName(name) + ModuleTreeitem::ModuleTreeitem(ItemType itp, int id, const QString &name, const QString &ntp) + : mItemType(itp), mId(id), mName(name), mNodeType(ntp) {;} QVariant ModuleTreeitem::getData(int index) const { switch (index) { - case 0: { - QVariant qvName = QVariant(QString::fromStdString(mName)); - return qvName; - break;} - case 1: { - QVariant qvId = QVariant(mId); - return qvId; - break;} - case 2: { - QVariant qvType = QVariant(QString::fromStdString(mType)); - return qvType; - break;} + case 0: + return mName; + case 1: + return mId; + case 2: + return mNodeType; } } void ModuleTreeitem::setData(QList data) { - mName = data[0].toString().toStdString(); + mName = data[0].toString(); mId = data[1].toInt(); - mType = data[2].toString().toStdString(); + mNodeType = data[2].toString(); } void ModuleTreeitem::setDataAtIndex(int index, QVariant &data) @@ -46,13 +40,13 @@ namespace hal switch (index) { - case 0: mName = data.toString().toStdString(); break; + case 0: mName = data.toString(); break; case 1: mId = data.toInt(); break; case 2: for (int j=0; j<3; j++) if (data.toString() == ctyp[j]) { - mType = data.toString().toStdString(); + mNodeType = data.toString(); break; } } @@ -101,21 +95,21 @@ namespace hal //add modules for(auto mod : m->get_submodules()) { - ModuleTreeitem* modItem = new ModuleTreeitem(mod->get_name(), - mod->get_id(), mod->get_type()); + ModuleTreeitem* modItem = new ModuleTreeitem(ModuleTreeitem::Module, + mod->get_id(), + QString::fromStdString(mod->get_name()), + QString::fromStdString(mod->get_type())); moduleRecursive(mod, modItem); - modItem->setAdditionalData(mKeyItemType, QVariant::fromValue(itemType::module)); - modItem->setAdditionalData(mKeyRepId, mod->get_id()); mRootItem->appendChild(modItem); mModuleToTreeitems.insert(mod, modItem); } //add gates for(auto gate : m->get_gates()) { - ModuleTreeitem* gateItem = new ModuleTreeitem(gate->get_name(), - gate->get_id(), gate->get_type()->get_name()); - gateItem->setAdditionalData(mKeyItemType, QVariant::fromValue(itemType::gate)); - gateItem->setAdditionalData(mKeyRepId, gate->get_id()); + ModuleTreeitem* gateItem = new ModuleTreeitem(ModuleTreeitem::Gate, + gate->get_id(), + QString::fromStdString(gate->get_name()), + QString::fromStdString(gate->get_type()->get_name())); mRootItem->appendChild(gateItem); mGateToTreeitems.insert(gate, gateItem); } @@ -138,42 +132,37 @@ namespace hal if(!index.isValid()) return QVariant(); - BaseTreeItem* item = getItemFromIndex(index); + ModuleTreeitem* item = dynamic_cast(getItemFromIndex(index)); if(!item) return QVariant(); if(role == Qt::DecorationRole && index.column() == 0) - return getIconFromItem(getItemFromIndex(index)); + return getIconFromItem(item); //yes, it performs the same two checks again, should be okay though (in terms of performance) return BaseTreeModel::data(index, role); } - ModuleTreeModel::itemType ModuleTreeModel::getTypeOfItem(BaseTreeItem *item) const - { - return item->getAdditionalData(mKeyItemType).value(); - } - void ModuleTreeModel::moduleRecursive(Module *mod, BaseTreeItem *modItem) { ModuleTreeitem* subModItem = nullptr; for(Module* subMod : mod->get_submodules()) { - subModItem = new ModuleTreeitem(subMod->get_name(), - subMod->get_id(), subMod->get_type()); + subModItem = new ModuleTreeitem(ModuleTreeitem::Module, + subMod->get_id(), + QString::fromStdString(subMod->get_name()), + QString::fromStdString(subMod->get_type())); moduleRecursive(subMod, subModItem); - subModItem->setAdditionalData(mKeyItemType, QVariant::fromValue(itemType::module)); - subModItem->setAdditionalData(mKeyRepId, subMod->get_id()); modItem->appendChild(subModItem); mModuleToTreeitems.insert(subMod, subModItem); } for(auto gate : mod->get_gates()) { - ModuleTreeitem* gateItem = new ModuleTreeitem(gate->get_name(), - gate->get_id(), gate->get_type()->get_name()); - gateItem->setAdditionalData(mKeyItemType, QVariant::fromValue(itemType::gate)); - gateItem->setAdditionalData(mKeyRepId, gate->get_id()); + ModuleTreeitem* gateItem = new ModuleTreeitem(ModuleTreeitem::Gate, + gate->get_id(), + QString::fromStdString(gate->get_name()), + QString::fromStdString(gate->get_type()->get_name())); modItem->appendChild(gateItem); mGateToTreeitems.insert(gate, gateItem); } @@ -190,8 +179,10 @@ namespace hal //1. Find index of first gate-type item int startIndex = 0; for(; startIndex < modItem->getChildCount(); startIndex++) - if(getTypeOfItem(modItem->getChild(startIndex)) != itemType::module) + { + if(static_cast(modItem->getChild(startIndex))->itemType() != ModuleTreeitem::Module) break; + } beginResetModel(); @@ -213,10 +204,10 @@ namespace hal beginResetModel(); for(auto gate : mod->get_gates()) { - ModuleTreeitem* gateItem = new ModuleTreeitem(gate->get_name(), - gate->get_id(), gate->get_type()->get_name()); - gateItem->setAdditionalData(mKeyItemType, QVariant::fromValue(itemType::gate)); - gateItem->setAdditionalData(mKeyRepId, gate->get_id()); + ModuleTreeitem* gateItem = new ModuleTreeitem(ModuleTreeitem::Gate, + gate->get_id(), + QString::fromStdString(gate->get_name()), + QString::fromStdString(gate->get_type()->get_name())); modItem->appendChild(gateItem); mGateToTreeitems.insert(gate, gateItem); } @@ -224,17 +215,17 @@ namespace hal endResetModel(); } - QIcon ModuleTreeModel::getIconFromItem(BaseTreeItem *item) const + QIcon ModuleTreeModel::getIconFromItem(ModuleTreeitem *item) const { if(!item) return QIcon(); u32 id = item->getData(1).toInt(); - switch (getTypeOfItem(item)) + switch (item->itemType()) { - case itemType::module: + case ModuleTreeitem::Module: return QIcon(*SelectionDetailsIconProvider::instance()->getIcon(SelectionDetailsIconProvider::ModuleIcon,id)); - case itemType::gate: + case ModuleTreeitem::Gate: return QIcon(*SelectionDetailsIconProvider::instance()->getIcon(SelectionDetailsIconProvider::GateIcon,id)); default: return QIcon(); @@ -298,11 +289,11 @@ namespace hal { beginResetModel(); auto addedMod = gNetlist->get_module_by_id(added_module); - ModuleTreeitem* addedSubmodItem = new ModuleTreeitem(addedMod->get_name(), addedMod->get_id(), - addedMod->get_type()); + ModuleTreeitem* addedSubmodItem = new ModuleTreeitem(ModuleTreeitem::Module, + addedMod->get_id(), + QString::fromStdString(addedMod->get_name()), + QString::fromStdString(addedMod->get_type())); moduleRecursive(addedMod, addedSubmodItem); - addedSubmodItem->setAdditionalData(mKeyItemType, QVariant::fromValue(itemType::module)); - addedSubmodItem->setAdditionalData(mKeyRepId, addedMod->get_id()); parentModItem ? parentModItem->insertChild(0, addedSubmodItem) : mRootItem->insertChild(0, addedSubmodItem); mModuleToTreeitems.insert(addedMod, addedSubmodItem); endResetModel(); @@ -322,11 +313,11 @@ namespace hal treeItemsQueue.enqueue(removedModItem); while(!treeItemsQueue.isEmpty()) { - BaseTreeItem* current = treeItemsQueue.dequeue(); - switch (getTypeOfItem(current)) + ModuleTreeitem* current = static_cast(treeItemsQueue.dequeue()); + switch (current->itemType()) { - case itemType::module: mModuleToTreeitems.remove(gNetlist->get_module_by_id(current->getData(ModuleTreeModel::sIdColumn).toInt())); break; - case itemType::gate: mGateToTreeitems.remove(gNetlist->get_gate_by_id(current->getData(ModuleTreeModel::sIdColumn).toInt()));break; + case ModuleTreeitem::Module: mModuleToTreeitems.remove(gNetlist->get_module_by_id(current->getData(ModuleTreeModel::sIdColumn).toInt())); break; + case ModuleTreeitem::Gate: mGateToTreeitems.remove(gNetlist->get_gate_by_id(current->getData(ModuleTreeModel::sIdColumn).toInt()));break; } for(auto child : current->getChildren()) treeItemsQueue.enqueue(child); @@ -353,12 +344,13 @@ namespace hal auto assignedGate = gNetlist->get_gate_by_id(assigned_gate); int indexToInsert = 0; //first item after the modules for(; indexToInsert < modItem->getChildCount(); indexToInsert++) - if(getTypeOfItem(modItem->getChild(indexToInsert)) != itemType::module) + if(static_cast(modItem->getChild(indexToInsert))->itemType() != ModuleTreeitem::Module) break; - ModuleTreeitem* gateItem = new ModuleTreeitem(assignedGate->get_name(), - assignedGate->get_id(), assignedGate->get_type()->get_name()); - gateItem->setAdditionalData(mKeyItemType, QVariant::fromValue(itemType::gate)); + ModuleTreeitem* gateItem = new ModuleTreeitem(ModuleTreeitem::Gate, + assignedGate->get_id(), + QString::fromStdString(assignedGate->get_name()), + QString::fromStdString(assignedGate->get_type()->get_name())); mGateToTreeitems.insert(assignedGate, gateItem); //beginInsertRows(getIndexFromItem(modItem), indexToInsert, indexToInsert); beginResetModel(); diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index 2c4d45ff116..e2743ec7928 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -19,8 +19,8 @@ namespace hal { - PortTreeItem::PortTreeItem(QString pinName, QString pinDirection, QString pinTypee, QString netName) - : mPinName(pinName), mPinDirection(pinDirection), mPinType(pinTypee), mNetName(netName) + PortTreeItem::PortTreeItem(Type tp, QString pinName, QString pinDirection, QString pinTypee, QString netName) + : mType(tp), mPinName(pinName), mPinDirection(pinDirection), mPinType(pinTypee), mNetName(netName) {;} QVariant PortTreeItem::getData(int index) const @@ -146,7 +146,7 @@ namespace hal auto item = static_cast(getItemFromIndex(indexes.at(0))); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); - QString type = getTypeOfItem(item) == itemType::pin ? "pin" : "group"; + QString type = item->type() == PortTreeItem::Pin ? "pin" : "group"; stream << type << getIdOfItem(item); data->setText(item->getData(sNameColumn).toString()); data->setData("pintreemodel/item", encodedData); @@ -364,11 +364,10 @@ namespace hal // 5: drop adjecent index to itself, must be at least 1 item apart if(type == "group") { - auto item = mIdToGroupItem[id]; + auto item = static_cast(mIdToGroupItem[id]); if(parentItem) { - auto type = getTypeOfItem(parentItem); - if(type == itemType::pin || (type == itemType::group && row != -1) || item == parentItem) + if(item->type() == PortTreeItem::Pin || (item->type() == PortTreeItem::Group && row != -1) || item == parentItem) return false; } else // here, only check for adjacent rows @@ -384,7 +383,7 @@ namespace hal // otherwise it does not make much sense...perhaps change this check auto item = mIdToPinItem[id]; if((!parentItem && item->getParent()->getChildCount() == 1) || (item->getParent() == parentItem && row == -1) || item == parentItem - || (parentItem && (getTypeOfItem(parentItem) == itemType::pin))) + || (parentItem && (parentItem->type() == PortTreeItem::Pin))) return false; // case if one wants to drop between pins in same group, check if its not adjacent row (other cases are handled on case above if(item->getParent() == parentItem) @@ -423,7 +422,7 @@ namespace hal auto pinGroupDirection = QString::fromStdString(enum_to_string((pinGroup->get_direction()))); auto pinGroupType = QString::fromStdString(enum_to_string(pinGroup->get_type())); - PortTreeItem* pinGroupItem = new PortTreeItem(pinGroupName, pinGroupDirection, pinGroupType, ""); + PortTreeItem* pinGroupItem = new PortTreeItem(PortTreeItem::Group,pinGroupName, pinGroupDirection, pinGroupType, ""); pinGroupItem->setAdditionalData(keyType, QVariant::fromValue(itemType::group)); // port multi bit pinGroupItem->setAdditionalData(keyId, pinGroup->get_id()); mIdToGroupItem.insert(pinGroup->get_id(), pinGroupItem); From a97293d9989e8d3736f4b1622628b69acd96beee Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 14 Aug 2023 15:43:59 +0200 Subject: [PATCH 10/66] replace 'AdditionalData' from BaseTreeItem by more specific data fields --- .../gate_details_widget/pin_tree_model.h | 4 - .../netlist_elements_tree_model.h | 30 ++-- .../module_details_widget/port_tree_model.h | 23 ++- .../module_ports_tree.cpp | 18 +-- .../netlist_elements_tree_model.cpp | 135 ++++++++---------- .../module_details_widget/port_tree_model.cpp | 53 +++---- 6 files changed, 112 insertions(+), 151 deletions(-) diff --git a/plugins/gui/include/gui/selection_details_widget/gate_details_widget/pin_tree_model.h b/plugins/gui/include/gui/selection_details_widget/gate_details_widget/pin_tree_model.h index 9a938078830..81c06e3f4b2 100644 --- a/plugins/gui/include/gui/selection_details_widget/gate_details_widget/pin_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/gate_details_widget/pin_tree_model.h @@ -133,10 +133,6 @@ class GatePinsTreeModel : public BaseTreeModel static const int sTypeColumn = 2; static const int sConnectedNetColumn = 3; - //additional data keys - const QString keyType = "type"; - const QString keyRepresentedNetsID = "netID"; //might not be needed - private: int mGateId; QMap mPinGroupToTreeItem; diff --git a/plugins/gui/include/gui/selection_details_widget/module_details_widget/netlist_elements_tree_model.h b/plugins/gui/include/gui/selection_details_widget/module_details_widget/netlist_elements_tree_model.h index 5bb90aa7240..b6540d1855e 100644 --- a/plugins/gui/include/gui/selection_details_widget/module_details_widget/netlist_elements_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/module_details_widget/netlist_elements_tree_model.h @@ -29,6 +29,7 @@ #include //#include "gui/new_selection_details_widget/models/base_tree_item.h" #include "gui/basic_tree_model/base_tree_model.h" +#include "hal_core/defines.h" namespace hal { @@ -39,19 +40,30 @@ namespace hal class NetlistElementsTreeitem : public BaseTreeItem { + public: + enum ItemType { None, Module, Gate, Net}; private: - QString mType; - int mId; + ItemType mItemType; + u32 mId; QString mName; + QString mNodeType; public: - NetlistElementsTreeitem(const QString& name, int id, QString tp); + NetlistElementsTreeitem(ItemType itp, u32 id_, const QString& name, const QString& ntp = QString()); QVariant getData(int column) const override; void setData(QList data) override; void setDataAtIndex(int index, QVariant& data) override; void appendData(QVariant data) override; int getColumnCount() const override; + + u32 id() const { return mId; } + /** + * Get the type (enum) of a given item. + * + * @return The item's type. + */ + ItemType itemType() const { return mItemType; } }; /** @@ -63,8 +75,6 @@ namespace hal Q_OBJECT public: - //metatype declaration at the end of file - enum class itemType {module = 0, gate = 1, net = 2}; /** * The constructor. @@ -121,14 +131,6 @@ namespace hal */ void setModule(Module* mod, bool showGates = true, bool showNets = true, bool displayModulesRecursive = true); - /** - * Get the type (enum) of a given item. - * - * @param item - The item for which the type is requested. - * @return The item's type. - */ - itemType getTypeOfItem(NetlistElementsTreeitem* item) const; - /** * Get the module/gate/net id that the given item represents. * To know the type of the item, call getTypeOfItem(). @@ -217,5 +219,3 @@ namespace hal }; } - -Q_DECLARE_METATYPE(hal::NetlistElementsTreeModel::itemType) diff --git a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h index 17080bafb79..45d5b02b91e 100644 --- a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h @@ -57,9 +57,17 @@ namespace hal void setDataAtIndex(int index, QVariant& data) override; void appendData(QVariant data) override; int getColumnCount() const override; - setType(Type tp) { mType = tp; } + void setType(Type tp) { mType = tp; } Type type() const { return mType; } void setId(u32 id_) { mId = id_; } + + /** + * Returns the pin-id if the item represents a pin or the pingroup-id + * if the item represents a pingroup. + * + * @param item - The item. + * @return The pin- or pingroup-id. + */ u32 id() const { return mId; } }; @@ -121,15 +129,6 @@ namespace hal */ int getRepresentedModuleId(); - /** - * Returns the pin-id if the item represents a pin or the pingroup-id - * if the item represents a pingroup. - * - * @param item - The item. - * @return The pin- or pingroup-id. - */ - int getIdOfItem(BaseTreeItem* item) const; - /** @name Event Handler Functions */ ///@{ @@ -142,10 +141,6 @@ namespace hal static const int sTypeColumn = 2; static const int sNetColumn = 3; - //additional data keys - const QString keyType = "type"; - const QString keyId = "id"; - Q_SIGNALS: /** * Q_SIGNAL that is emitted when the number of the model's port changed. diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp index f2f4e988463..7b2c60d973e 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp @@ -97,7 +97,7 @@ namespace hal std::pair sameGroup; bool onlyPins; std::tie(selectedPins, sameGroup, onlyPins) = getSelectedPins(); - int itemId = mPortModel->getIdOfItem(clickedItem); + int itemId = clickedItem->id(); QMenu menu; //shared plaintext entries: NAME, DIRECTION, TYPE (shared with pins and groups) @@ -119,7 +119,7 @@ namespace hal // } if (addToExistingActionPossible) { - menu.addAction("Add selection to existing pin group", [this, selectedPins, mod]() { + menu.addAction("Add selection to existing pin group", [selectedPins, mod]() { PingroupSelectorDialog psd("Pingroup selector", "Select pingroup", mod, false); if (psd.exec() == QDialog::Accepted) { @@ -129,7 +129,7 @@ namespace hal return; for (auto item : selectedPins) { - auto* pin = mod->get_pin_by_id(mPortModel->getIdOfItem(item)); + auto* pin = mod->get_pin_by_id(static_cast(item)->id()); if (pin == nullptr) return; pins.append(pin->get_id()); @@ -229,10 +229,10 @@ namespace hal //can be both single(simple right-click, no real selection) and multi-selection if (sameGroup.first && mod->get_pin_group_by_id(sameGroup.second)->size() > 1) { - menu.addAction("Remove selection from group", [this, selectedPins, mod /*, sameGroup*/]() { + menu.addAction("Remove selection from group", [selectedPins, mod /*, sameGroup*/]() { QList pins; for (auto item : selectedPins) - pins.append(mPortModel->getIdOfItem(item)); + pins.append(static_cast(item)->id()); ActionPingroup* act = ActionPingroup::removePinsFromGroup(mod, pins); if (act) act->exec(); @@ -266,7 +266,7 @@ namespace hal std::tie(selectedPins, sameGroup, onlyPins) = getSelectedPins(); if (selectedPins.size() > 1) { - menu.addAction("Add objects to new pin group", [this, selectedPins, modId]() { + menu.addAction("Add objects to new pin group", [selectedPins, modId]() { InputDialog ipd("Pingroup name", "New pingroup name", "ExampleName"); if (ipd.exec() == QDialog::Accepted && !ipd.textValue().isEmpty()) { @@ -274,7 +274,7 @@ namespace hal Module* mod = gNetlist->get_module_by_id(modId); for (auto item : selectedPins) { - auto* pin = mod->get_pin_by_id(mPortModel->getIdOfItem(item)); + auto* pin = mod->get_pin_by_id(static_cast(item)->id()); if (pin == nullptr) return; pins.append(pin->get_id()); @@ -322,11 +322,11 @@ namespace hal if (!selectedPins.isEmpty()) { auto mod = gNetlist->get_module_by_id(mModuleID); - auto* firstPin = mod->get_pin_by_id(mPortModel->getIdOfItem(selectedPins.front())); + auto* firstPin = mod->get_pin_by_id(static_cast(selectedPins.front())->id()); groupId = firstPin->get_group().first->get_id(); for (auto pinTreeItem : selectedPins) { - auto pin = mod->get_pin_by_id(mPortModel->getIdOfItem(pinTreeItem)); + auto pin = mod->get_pin_by_id(static_cast(pinTreeItem)->id()); if (groupId != (int)pin->get_group().first->get_id()) { sameGroup = false; diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/netlist_elements_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/netlist_elements_tree_model.cpp index 536d24d6103..b20df579034 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/netlist_elements_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/netlist_elements_tree_model.cpp @@ -10,34 +10,29 @@ namespace hal { - NetlistElementsTreeitem::NetlistElementsTreeitem(const QString &name, int id, QString tp) - : mType(tp), mId(id), mName(name) + NetlistElementsTreeitem::NetlistElementsTreeitem(ItemType itp, u32 id_, const QString &name, const QString &ntp) + : mItemType(itp), mId(id_), mName(name), mNodeType(ntp) {;} QVariant NetlistElementsTreeitem::getData(int index) const { switch (index) { - case 0: { - QVariant qvName = QVariant(mName); - return qvName; - break;} - case 1: { - QVariant qvId = QVariant(mId); - return qvId; - break;} - case 2: { - QVariant qvType = QVariant(mType); - return qvType; - break;} + case 0: + return mName; + case 1: + return mId; + case 2: + return mNodeType; } + return QVariant(); } void NetlistElementsTreeitem::setData(QList data) { mName = data[0].toString(); mId = data[1].toInt(); - mType = data[2].toString(); + mNodeType = data[2].toString(); } @@ -53,7 +48,7 @@ namespace hal for (int j=0; j<3; j++) if (data.toString() == ctyp[j]) { - mType = data.toString(); + mNodeType = data.toString(); break; } } @@ -145,12 +140,12 @@ namespace hal Module* mod = gNetlist->get_module_by_id(id); if(!mod) continue; - NetlistElementsTreeitem* modItem = new NetlistElementsTreeitem(QString::fromStdString(mod->get_name()), - mod->get_id(), QString::fromStdString(mod->get_type())); + NetlistElementsTreeitem* modItem = new NetlistElementsTreeitem(NetlistElementsTreeitem::Module, + mod->get_id(), + QString::fromStdString(mod->get_name()), + QString::fromStdString(mod->get_type())); if(displayModulesRecursive) moduleRecursive(mod, modItem, showGatesInSubmods, showNetsInSubmods); - modItem->setAdditionalData(keyItemType, QVariant::fromValue(itemType::module)); - modItem->setAdditionalData(keyRepresentedID, mod->get_id()); mRootItem->appendChild(modItem); mModuleToTreeitems.insert(mod, modItem); } @@ -158,20 +153,19 @@ namespace hal for(int id : gateIds) { Gate* gate = gNetlist->get_gate_by_id(id); - NetlistElementsTreeitem* gateItem = new NetlistElementsTreeitem(QString::fromStdString(gate->get_name()), - gate->get_id(), QString::fromStdString(gate->get_type()->get_name())); - gateItem->setAdditionalData(keyItemType, QVariant::fromValue(itemType::gate)); - gateItem->setAdditionalData(keyRepresentedID, gate->get_id()); + NetlistElementsTreeitem* gateItem = new NetlistElementsTreeitem(NetlistElementsTreeitem::Gate, + gate->get_id(), + QString::fromStdString(gate->get_name()), + QString::fromStdString(gate->get_type()->get_name())); mRootItem->appendChild(gateItem); mGateToTreeitems.insert(gate, gateItem); } for(int id : netIds) { Net* net = gNetlist->get_net_by_id(id); - NetlistElementsTreeitem* netItem = new NetlistElementsTreeitem(QString::fromStdString(net->get_name()), - net->get_id(), ""); - netItem->setAdditionalData(keyItemType, QVariant::fromValue(itemType::net)); - netItem->setAdditionalData(keyRepresentedID, net->get_id()); + NetlistElementsTreeitem* netItem = new NetlistElementsTreeitem(NetlistElementsTreeitem::Net, + net->get_id(), + QString::fromStdString(net->get_name())); mRootItem->appendChild(netItem); mNetToTreeitems.insert(net, netItem); } @@ -199,16 +193,6 @@ namespace hal Q_EMIT numberOfSubmodulesChanged(mod->get_submodules().size()); } - NetlistElementsTreeModel::itemType NetlistElementsTreeModel::getTypeOfItem(NetlistElementsTreeitem *item) const - { - return item->getAdditionalData(keyItemType).value(); - } - - int NetlistElementsTreeModel::getRepresentedIdOfItem(NetlistElementsTreeitem *item) const - { - return item->getAdditionalData(keyRepresentedID).toInt(); - } - void NetlistElementsTreeModel::gateNameChanged(Gate *g) { for(BaseTreeItem* gateItem : mGateToTreeitems.values(g)) @@ -302,13 +286,13 @@ namespace hal while(!treeItemsQueue.isEmpty()) { NetlistElementsTreeitem* currentItem = treeItemsQueue.dequeue(); - int id = currentItem->getAdditionalData(keyRepresentedID).toUInt(); + int id = currentItem->id(); - switch (getTypeOfItem(currentItem)) + switch (currentItem->itemType()) { - case itemType::module: mModuleToTreeitems.remove(gNetlist->get_module_by_id(id),currentItem); break; - case itemType::gate: mGateToTreeitems.remove(gNetlist->get_gate_by_id(id), currentItem); break; - case itemType::net: mNetToTreeitems.remove(gNetlist->get_net_by_id(id), currentItem); break; + case NetlistElementsTreeitem::Module : mModuleToTreeitems.remove(gNetlist->get_module_by_id(id),currentItem); break; + case NetlistElementsTreeitem::Gate : mGateToTreeitems.remove(gNetlist->get_gate_by_id(id), currentItem); break; + case NetlistElementsTreeitem::Net : mNetToTreeitems.remove(gNetlist->get_net_by_id(id), currentItem); break; } for(BaseTreeItem* child : currentItem->getChildren()){ @@ -349,14 +333,14 @@ namespace hal int indexToInsert = 0; for(; indexToInsert < modItem->getChildCount(); indexToInsert++) { NetlistElementsTreeitem* neti = static_cast(modItem->getChild(indexToInsert)); - if(getTypeOfItem(neti) != itemType::module) + if(neti->itemType() != NetlistElementsTreeitem::Module) break; } - NetlistElementsTreeitem* gateItem = new NetlistElementsTreeitem(QString::fromStdString(assignedGate->get_name()), - assignedGate->get_id(), QString::fromStdString(assignedGate->get_type()->get_name())); - gateItem->setAdditionalData(keyItemType, QVariant::fromValue(itemType::gate)); - gateItem->setAdditionalData(keyRepresentedID, assignedGate->get_id()); + NetlistElementsTreeitem* gateItem = new NetlistElementsTreeitem(NetlistElementsTreeitem::Gate, + assignedGate->get_id(), + QString::fromStdString(assignedGate->get_name()), + QString::fromStdString(assignedGate->get_type()->get_name())); //beginInsertRows(getIndexFromItem(modItem), indexToInsert, indexToInsert); beginResetModel(); modItem->insertChild(indexToInsert, gateItem); @@ -393,11 +377,11 @@ namespace hal Module* addedModule = gNetlist->get_module_by_id(added_module); auto appendNewSubmodItem = [this, addedModule](BaseTreeItem* parentModItem){ - NetlistElementsTreeitem* addedSubmodItem = new NetlistElementsTreeitem(QString::fromStdString(addedModule->get_name()), - addedModule->get_id(), QString::fromStdString(addedModule->get_type())); + NetlistElementsTreeitem* addedSubmodItem = new NetlistElementsTreeitem(NetlistElementsTreeitem::Module, + addedModule->get_id(), + QString::fromStdString(addedModule->get_name()), + QString::fromStdString(addedModule->get_type())); moduleRecursive(addedModule, addedSubmodItem, mGatesDisplayed, mNetsDisplayed); - addedSubmodItem->setAdditionalData(keyItemType, QVariant::fromValue(itemType::module)); - addedSubmodItem->setAdditionalData(keyRepresentedID, addedModule->get_id()); parentModItem->insertChild(0, addedSubmodItem); mModuleToTreeitems.insert(addedModule, addedSubmodItem); if(mNetsDisplayed) { @@ -425,11 +409,11 @@ namespace hal NetlistElementsTreeitem* subModItem = nullptr; for(Module* subMod : mod->get_submodules()) { - subModItem = new NetlistElementsTreeitem(QString::fromStdString(subMod->get_name()), - subMod->get_id(), QString::fromStdString(subMod->get_type())); + subModItem = new NetlistElementsTreeitem(NetlistElementsTreeitem::Module, + subMod->get_id(), + QString::fromStdString(subMod->get_name()), + QString::fromStdString(subMod->get_type())); moduleRecursive(subMod, subModItem, showGates); - subModItem->setAdditionalData(keyItemType, QVariant::fromValue(itemType::module)); - subModItem->setAdditionalData(keyRepresentedID, subMod->get_id()); modItem->appendChild(subModItem); mModuleToTreeitems.insert(subMod, subModItem); } @@ -437,10 +421,10 @@ namespace hal { for(auto gate : mod->get_gates()) { - NetlistElementsTreeitem* gateItem = new NetlistElementsTreeitem(QString::fromStdString(gate->get_name()), - gate->get_id(), QString::fromStdString(gate->get_type()->get_name())); - gateItem->setAdditionalData(keyItemType, QVariant::fromValue(itemType::gate)); - gateItem->setAdditionalData(keyRepresentedID, gate->get_id()); + NetlistElementsTreeitem* gateItem = new NetlistElementsTreeitem(NetlistElementsTreeitem::Gate, + gate->get_id(), + QString::fromStdString(gate->get_name()), + QString::fromStdString(gate->get_type()->get_name())); modItem->appendChild(gateItem); mGateToTreeitems.insert(gate, gateItem); } @@ -449,10 +433,10 @@ namespace hal { for(auto net : mod->get_internal_nets()) { - NetlistElementsTreeitem* netItem = new NetlistElementsTreeitem(QString::fromStdString(net->get_name()), - net->get_id(), ""); - netItem->setAdditionalData(keyItemType, QVariant::fromValue(itemType::net)); - netItem->setAdditionalData(keyRepresentedID, net->get_id()); + NetlistElementsTreeitem* netItem = new NetlistElementsTreeitem(NetlistElementsTreeitem::Net, + net->get_id(), + QString::fromStdString(net->get_name()), + ""); modItem->appendChild(netItem); mNetToTreeitems.insert(net, netItem); } @@ -465,11 +449,11 @@ namespace hal return QIcon(); u32 id = item->getData(1).toInt(); - switch (getTypeOfItem(item)) + switch (item->itemType()) { - case itemType::module: + case NetlistElementsTreeitem::Module: return QIcon(*SelectionDetailsIconProvider::instance()->getIcon(SelectionDetailsIconProvider::ModuleIcon,id)); - case itemType::gate: + case NetlistElementsTreeitem::Gate: return QIcon(*SelectionDetailsIconProvider::instance()->getIcon(SelectionDetailsIconProvider::GateIcon,id)); default: return QIcon(); @@ -479,27 +463,26 @@ namespace hal void NetlistElementsTreeModel::updateInternalNetsOfModule(NetlistElementsTreeitem *moduleItem) { BaseTreeItem* moduleItemBase = static_cast(moduleItem); - BaseTreeItem* mRootBase = static_cast(mRootItem); - int moduleId = (moduleItemBase == mRootBase) ? mModId : moduleItem->getAdditionalData(keyRepresentedID).toInt(); + BaseTreeItem* mRootBase = static_cast(mRootItem); + int moduleId = (moduleItemBase == mRootBase) ? mModId : moduleItem->id(); Module* mod = gNetlist->get_module_by_id(moduleId); //remove and delte the last child of the module-item until no net items are left - while(moduleItem->getChildCount() > 0 && getTypeOfItem(static_cast(moduleItem->getChild(moduleItem->getChildCount()-1))) == itemType::net) + while(moduleItem->getChildCount() > 0 && static_cast(moduleItem->getChild(moduleItem->getChildCount()-1))->itemType() == NetlistElementsTreeitem::Net) { NetlistElementsTreeitem* lastNetItem = static_cast(moduleItem->removeChildAtPos(moduleItem->getChildCount()-1)); - mNetToTreeitems.remove(gNetlist->get_net_by_id(lastNetItem->getAdditionalData(keyRepresentedID).toUInt()), lastNetItem); + mNetToTreeitems.remove(gNetlist->get_net_by_id(lastNetItem->id()), lastNetItem); delete lastNetItem; } //append (potentionally) new internal nets for(Net* n : mod->get_internal_nets()) { - NetlistElementsTreeitem* netItem = new NetlistElementsTreeitem(QString::fromStdString(n->get_name()),n->get_id(), ""); + NetlistElementsTreeitem* netItem = new NetlistElementsTreeitem(NetlistElementsTreeitem::Net, + n->get_id(), + QString::fromStdString(n->get_name()), + ""); //netItem->setAdditionalData(keyItemType, itemType::net); - netItem->setAdditionalData(keyItemType, QVariant::fromValue(itemType::net)); - netItem->setAdditionalData(keyRepresentedID, n->get_id()); mNetToTreeitems.insert(n, netItem); moduleItem->appendChild(netItem); } } - - } diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index e2743ec7928..0b0fe67cfb2 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -143,11 +143,11 @@ namespace hal return new QMimeData(); QMimeData* data = new QMimeData(); - auto item = static_cast(getItemFromIndex(indexes.at(0))); + PortTreeItem* item = static_cast(getItemFromIndex(indexes.at(0))); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QString type = item->type() == PortTreeItem::Pin ? "pin" : "group"; - stream << type << getIdOfItem(item); + stream << type << item->id(); data->setText(item->getData(sNameColumn).toString()); data->setData("pintreemodel/item", encodedData); return data; @@ -423,17 +423,16 @@ namespace hal auto pinGroupType = QString::fromStdString(enum_to_string(pinGroup->get_type())); PortTreeItem* pinGroupItem = new PortTreeItem(PortTreeItem::Group,pinGroupName, pinGroupDirection, pinGroupType, ""); - pinGroupItem->setAdditionalData(keyType, QVariant::fromValue(itemType::group)); // port multi bit - pinGroupItem->setAdditionalData(keyId, pinGroup->get_id()); + pinGroupItem->setId(pinGroup->get_id()); mIdToGroupItem.insert(pinGroup->get_id(), pinGroupItem); for(ModulePin* pin : pinGroup->get_pins()) { - PortTreeItem* pinItem = new PortTreeItem(QString::fromStdString(pin->get_name()), - QString::fromStdString(enum_to_string(pin->get_direction())), - QString::fromStdString(enum_to_string(pin->get_type())), - QString::fromStdString(pin->get_net()->get_name())); - pinItem->setAdditionalData(keyType, QVariant::fromValue(itemType::pin)); - pinItem->setAdditionalData(keyId, pin->get_id()); + PortTreeItem* pinItem = new PortTreeItem(PortTreeItem::Pin, + QString::fromStdString(pin->get_name()), + QString::fromStdString(enum_to_string(pin->get_direction())), + QString::fromStdString(enum_to_string(pin->get_type())), + QString::fromStdString(pin->get_net()->get_name())); + pinItem->setId(pin->get_id()); pinGroupItem->appendChild(pinItem); mNameToTreeItem.insert(QString::fromStdString(pin->get_name()), pinItem); mIdToPinItem.insert(pin->get_id(), pinItem); @@ -501,8 +500,7 @@ namespace hal if (mModuleId == -1) //no current module = no represented net return nullptr; - itemType type = getTypeOfItem(item); - if (type == itemType::portMultiBit) + if (item->type() == PortTreeItem::Group && item->getChildCount() > 1) return nullptr; Module* m = gNetlist->get_module_by_id(mModuleId); @@ -510,7 +508,7 @@ namespace hal return nullptr; //std::string name = item->getData(sNameColumn).toString().toStdString(); - if (auto* pin = m->get_pin_by_id(getIdOfItem(item)); pin != nullptr) + if (auto* pin = m->get_pin_by_id(item->id()); pin != nullptr) { return pin->get_net(); } @@ -523,16 +521,6 @@ namespace hal return mModuleId; } - ModulePinsTreeModel::itemType ModulePinsTreeModel::getTypeOfItem(PortTreeItem* item) const - { - return item->getAdditionalData(keyType).value(); - } - - int ModulePinsTreeModel::getIdOfItem(BaseTreeItem* item) const - { - return item->getAdditionalData(keyId).toInt(); - } - void ModulePinsTreeModel::handleModulePortsChanged(Module* m, PinEvent pev, u32 pgid) { Q_UNUSED(pev); @@ -552,12 +540,12 @@ namespace hal // if(ipd.exec() == QDialog::Rejected) return false; mIgnoreEventsFlag = true; QList pins; - auto srcgroup = mModule->get_pin_group_by_id(getIdOfItem(droppedGroup)); + auto srcgroup = mModule->get_pin_group_by_id(static_cast(droppedGroup)->id()); for(const auto &pin : srcgroup->get_pins()) pins.append(pin->get_id()); if (pins.isEmpty()) return; // no pins to move - auto tgtgroup = mModule->get_pin_group_by_id(getIdOfItem(onDroppedGroup)); + auto tgtgroup = mModule->get_pin_group_by_id(static_cast(onDroppedGroup)->id()); ActionPingroup* act = ActionPingroup::addPinsToExistingGroup(mModule,tgtgroup->get_id(),pins); if (act) act->exec(); @@ -576,7 +564,7 @@ namespace hal bool bottomEdge = row == mRootItem->getChildCount(); auto desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; - ActionPingroup* act = new ActionPingroup(PinActionType::GroupMove,getIdOfItem(droppedGroup),"",desiredIdx); + ActionPingroup* act = new ActionPingroup(PinActionType::GroupMove,droppedGroup->id(),"",desiredIdx); act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); bool ok = act->exec(); if(ok){ @@ -589,9 +577,8 @@ namespace hal void ModulePinsTreeModel::dndPinOnGroup(PortTreeItem *droppedPin, BaseTreeItem *onDroppedGroup) { mIgnoreEventsFlag = true; - u32 pinId = getIdOfItem(droppedPin); - ActionPingroup* act = new ActionPingroup(PinActionType::PinAsignGroup,pinId,"",getIdOfItem(onDroppedGroup)); + ActionPingroup* act = new ActionPingroup(PinActionType::PinAsignGroup,droppedPin->id(),"",static_cast(onDroppedGroup)->id()); act->exec(); auto oldParent = droppedPin->getParent(); removeItem(droppedPin); @@ -615,11 +602,11 @@ namespace hal bool bottomEdge = row == onDroppedParent->getChildCount(); desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; // insert item here - act = new ActionPingroup(PinActionType::PinSetindex,getIdOfItem(droppedPin),"",desiredIdx); // TODO : start_index, descending + act = new ActionPingroup(PinActionType::PinSetindex,droppedPin->id(),"",desiredIdx); // TODO : start_index, descending } else { - act = ActionPingroup::addPinToExistingGroup(mModule,getIdOfItem(onDroppedParent),getIdOfItem(droppedPin),desiredIdx); + act = ActionPingroup::addPinToExistingGroup(mModule,static_cast(onDroppedParent)->id(),droppedPin->id(),desiredIdx); if (!act) return; } act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); @@ -641,7 +628,7 @@ namespace hal Q_UNUSED(row) mIgnoreEventsFlag = true; - auto pinToMove = mModule->get_pin_by_id(getIdOfItem(droppedPin)); + auto pinToMove = mModule->get_pin_by_id(droppedPin->id()); if (!pinToMove) return; QString groupName = QString::fromStdString(pinToMove->get_name()); @@ -687,7 +674,7 @@ namespace hal parent->insertChild(index, item); endInsertRows(); //mNameToTreeItem.insert(item->getData(sNameColumn).toString(), item); - getTypeOfItem(item) == itemType::pin ? mIdToPinItem.insert(getIdOfItem(item), item) : mIdToGroupItem.insert(getIdOfItem(item), item); + item->type() == PortTreeItem::Pin ? mIdToPinItem.insert(item->id(), item) : mIdToGroupItem.insert(item->id(), item); //mIdToPinItem.insert(getIdOfItem(item), item); } void ModulePinsTreeModel::removeItem(PortTreeItem* item) @@ -697,7 +684,7 @@ namespace hal endRemoveRows(); //mNameToTreeItem.remove(item->getData(sNameColumn).toString()); //for now, only ids of pin-items (since these functions are only relevant for dnd) - getTypeOfItem(item) == itemType::pin ? mIdToPinItem.remove(getIdOfItem(item)) : mIdToGroupItem.remove(getIdOfItem(item)); + item->type() == PortTreeItem::Pin ? mIdToPinItem.remove(item->id()) : mIdToGroupItem.remove(item->id()); //mIdToPinItem.remove(getIdOfItem(item)); //delete item; } From c46a098e631f7014ddb7546a71b1ec6fbb422f9d Mon Sep 17 00:00:00 2001 From: joern274 Date: Thu, 7 Sep 2023 20:43:10 +0200 Subject: [PATCH 11/66] Replace Result with bool --- .../hal_core/netlist/gate_library/gate_type.h | 6 +- include/hal_core/netlist/module.h | 24 +- .../src/plugin_bitorder_propagation.cpp | 5 +- plugins/dataflow_analysis/src/api/result.cpp | 14 +- .../module_details_widget/port_tree_model.cpp | 2 +- .../action_add_items_to_object.cpp | 2 +- .../gui/src/user_action/action_pingroup.cpp | 8 +- .../action_remove_items_from_object.cpp | 2 +- .../src/user_action/action_reorder_object.cpp | 3 +- .../netlist_modification_decorator.cpp | 7 +- src/netlist/gate_library/gate_type.cpp | 35 +-- src/netlist/module.cpp | 205 ++++++++++-------- src/python_bindings/bindings/gate_type.cpp | 5 +- src/python_bindings/bindings/module.cpp | 25 +-- tests/netlist/module.cpp | 32 +-- 15 files changed, 193 insertions(+), 182 deletions(-) diff --git a/include/hal_core/netlist/gate_library/gate_type.h b/include/hal_core/netlist/gate_library/gate_type.h index 34399a46661..17fcdf81893 100644 --- a/include/hal_core/netlist/gate_library/gate_type.h +++ b/include/hal_core/netlist/gate_library/gate_type.h @@ -354,9 +354,9 @@ namespace hal * Delete the given pin group. * * @param[in] pin_group - The pin group to be deleted. - * @returns Ok on success, an error message otherwise. + * @returns true on success, false otherwise. */ - Result delete_pin_group(PinGroup* pin_group); + bool delete_pin_group(PinGroup* pin_group); /** * Assign a pin to a pin group. @@ -469,6 +469,6 @@ namespace hal GateType& operator=(const GateType&) = delete; Result*> create_pin_group_internal(const u32 id, const std::string& name, PinDirection direction, PinType type, bool ascending, u32 start_index); - Result delete_pin_group_internal(PinGroup* pin_group); + bool delete_pin_group_internal(PinGroup* pin_group); }; } // namespace hal diff --git a/include/hal_core/netlist/module.h b/include/hal_core/netlist/module.h index 02cabb44aa4..874102df6cc 100644 --- a/include/hal_core/netlist/module.h +++ b/include/hal_core/netlist/module.h @@ -519,9 +519,9 @@ namespace hal * Delete the given pin group. * * @param[in] pin_group - The pin group to be deleted. - * @returns Ok on success, an error message otherwise. + * @returns true on success, false otherwise. */ - Result delete_pin_group(PinGroup* pin_group); + bool delete_pin_group(PinGroup* pin_group); /** * Move a pin group to another index within the module. @@ -531,7 +531,7 @@ namespace hal * @param[in] new_index - The index to which the pin group is moved. * @returns Ok on success, an error message otherwise. */ - Result move_pin_group(PinGroup* pin_group, u32 new_index); + bool move_pin_group(PinGroup* pin_group, u32 new_index); /** * Set the name of the given pin group. @@ -566,9 +566,9 @@ namespace hal * @param[in] pin_group - The new pin group. * @param[in] pin - The pin to be added. * @param[in] delete_empty_groups - Set `true` to delete groups that are empty after the pin has been assigned to the new group, `false` to keep empty groups. Defaults to `true`. - * @returns Ok on success, an error message otherwise. + * @returns `true` on success, `false` otherwise. */ - Result assign_pin_to_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups = true); + bool assign_pin_to_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups = true); /** * Move a pin to another index within the given pin group. @@ -577,9 +577,9 @@ namespace hal * @param[in] pin_group - The pin group. * @param[in] pin - The pin to be moved. * @param[in] new_index - The index to which the pin is moved. - * @returns Ok on success, an error message otherwise. + * @returns `true` on success, `false` otherwise. */ - Result move_pin_within_group(PinGroup* pin_group, ModulePin* pin, u32 new_index); + bool move_pin_within_group(PinGroup* pin_group, ModulePin* pin, u32 new_index); /** * Remove a pin from a pin group. @@ -588,9 +588,9 @@ namespace hal * @param[in] pin_group - The old pin group. * @param[in] pin - The pin to be removed. * @param[in] delete_empty_groups - Set `true` to delete the group of it is empty after the pin has been removed, `false` to keep the empty group. Defaults to `true`. - * @returns Ok on success, an error message otherwise. + * @returns `true` on success, `false` otherwise. */ - Result remove_pin_from_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups = true); + bool remove_pin_from_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups = true); /* * ################################################################ @@ -736,10 +736,10 @@ namespace hal NetConnectivity check_net_endpoints(const Net* net) const; Result check_net(Net* net, bool recursive = false); Result assign_pin_net(const u32 pin_id, Net* net, PinDirection direction, const std::string& name = "", PinType type = PinType::none); - Result remove_pin_net(Net* net); + bool remove_pin_net(Net* net); Result create_pin_internal(const u32 id, const std::string& name, Net* net, PinDirection direction, PinType type); - Result delete_pin_internal(ModulePin* pin); + bool delete_pin_internal(ModulePin* pin); Result*> create_pin_group_internal(const u32 id, const std::string& name, PinDirection direction, PinType type, bool ascending, u32 start_index); - Result delete_pin_group_internal(PinGroup* pin_group); + bool delete_pin_group_internal(PinGroup* pin_group); }; } // namespace hal diff --git a/plugins/bitorder_propagation/src/plugin_bitorder_propagation.cpp b/plugins/bitorder_propagation/src/plugin_bitorder_propagation.cpp index 32df78b9850..7e839e0d7ac 100644 --- a/plugins/bitorder_propagation/src/plugin_bitorder_propagation.cpp +++ b/plugins/bitorder_propagation/src/plugin_bitorder_propagation.cpp @@ -928,10 +928,9 @@ namespace hal for (const auto& [index, pin] : index_to_pin) { - auto move_res = m->move_pin_within_group(pg, pin, index); - if (move_res.is_error()) + if (!m->move_pin_within_group(pg, pin, index)) { - return ERR_APPEND(move_res.get_error(), + return ERR( "cannot reorder module pin groups: failed to move pin " + pin->get_name() + " in pin group " + pg->get_name() + " of module with ID " + std::to_string(m->get_id()) + " to new index " + std::to_string(index)); } diff --git a/plugins/dataflow_analysis/src/api/result.cpp b/plugins/dataflow_analysis/src/api/result.cpp index 1aecd57dcaf..d8441ec5c2c 100644 --- a/plugins/dataflow_analysis/src/api/result.cpp +++ b/plugins/dataflow_analysis/src/api/result.cpp @@ -519,9 +519,9 @@ namespace hal } else { - if (const auto res = new_mod->assign_pin_to_group(data_in_group, pin); res.is_error()) + if (!new_mod->assign_pin_to_group(data_in_group, pin)) { - log_warning("dataflow", "{}", res.get_error().get()); + log_warning("dataflow", "Assign pin to group failed."); } } @@ -542,9 +542,9 @@ namespace hal } else { - if (const auto res = new_mod->assign_pin_to_group(data_out_group, pin); res.is_error()) + if (!new_mod->assign_pin_to_group(data_out_group, pin)) { - log_warning("dataflow", "{}", res.get_error().get()); + log_warning("dataflow", "Assign pin to group failed."); } } @@ -588,9 +588,9 @@ namespace hal } else { - if (const auto res = new_mod->assign_pin_to_group(pin_group, pin); res.is_error()) + if (!new_mod->assign_pin_to_group(pin_group, pin)) { - log_warning("dataflow", "{}", res.get_error().get()); + log_warning("dataflow", "Assign pin to group failed."); } } @@ -814,4 +814,4 @@ namespace hal return OK(new_group_ids); } } // namespace dataflow -} // namespace hal \ No newline at end of file +} // namespace hal diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index 0b0fe67cfb2..84e15af7392 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -325,7 +325,7 @@ namespace hal // if (droppedParentItem != onDroppedItem) // { // mIgnoreEventsFlag = true; -// //int ret = mod->assign_pin_to_group(onDroppedGroup, droppedPin).is_ok(); +// //bool ret = mod->assign_pin_to_group(onDroppedGroup, droppedPin); // ActionAddItemsToObject* addAct = new ActionAddItemsToObject(QSet(), QSet(), QSet(), QSet() << droppedPin->get_id()); // addAct->setObject(UserActionObject(onDroppedGroup->get_id(), UserActionObjectType::PinGroup)); // addAct->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); diff --git a/plugins/gui/src/user_action/action_add_items_to_object.cpp b/plugins/gui/src/user_action/action_add_items_to_object.cpp index 25a201b5c0c..4ba1022590b 100644 --- a/plugins/gui/src/user_action/action_add_items_to_object.cpp +++ b/plugins/gui/src/user_action/action_add_items_to_object.cpp @@ -201,7 +201,7 @@ namespace hal for (auto id : mPins) { - if (mod->assign_pin_to_group(pinGrp, mod->get_pin_by_id(id), false).is_error()) + if (!mod->assign_pin_to_group(pinGrp, mod->get_pin_by_id(id), false)) { return false; } diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index 86c711b1878..a59affc1332 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -304,7 +304,7 @@ namespace hal int ptype = (int) pgroup->get_type(); int pdir = (int) pgroup->get_direction(); QString name = QString::fromStdString(pgroup->get_name()); - if (mParentModule->delete_pin_group(pgroup).is_error()) + if (!mParentModule->delete_pin_group(pgroup)) return false; addUndoAction(PinActionType::GroupCreate,id,name,v); addUndoAction(PinActionType::GroupTypechange,id,"",ptype); @@ -316,7 +316,7 @@ namespace hal int inx = pinGroupRow(mParentModule,pgroup); if (inx < 0) return false; addUndoAction(PinActionType::GroupMove,pgroup->get_id(),"",inx); - if (mParentModule->move_pin_group(pgroup,aa.mValue).is_error()) + if (!mParentModule->move_pin_group(pgroup,aa.mValue)) return false; break; } @@ -341,7 +341,7 @@ namespace hal mPinsMoved.insert(aa.mId); pgroup = getGroup(aa.mValue); if (!pgroup) return false; - if (mParentModule->assign_pin_to_group(pgroup,pin).is_error()) + if (!mParentModule->assign_pin_to_group(pgroup,pin)) return false; break; case PinActionType::PinRename: @@ -358,7 +358,7 @@ namespace hal if (!mPinsMoved.contains(aa.mId)) addUndoAction(PinActionType::PinSetindex,aa.mId,"",pin->get_group().second); pgroup = pin->get_group().first; - if (!mParentModule->move_pin_within_group(pgroup,pin,aa.mValue).is_ok()) + if (!mParentModule->move_pin_within_group(pgroup,pin,aa.mValue)) return false; break; default: diff --git a/plugins/gui/src/user_action/action_remove_items_from_object.cpp b/plugins/gui/src/user_action/action_remove_items_from_object.cpp index 79212b7e558..d5a20fc738b 100644 --- a/plugins/gui/src/user_action/action_remove_items_from_object.cpp +++ b/plugins/gui/src/user_action/action_remove_items_from_object.cpp @@ -136,7 +136,7 @@ namespace hal } for (u32 id : mPins) { - if (mod->remove_pin_from_group(pinGroup, mod->get_pin_by_id(id), false).is_error()) + if (!mod->remove_pin_from_group(pinGroup, mod->get_pin_by_id(id), false)) { return false; } diff --git a/plugins/gui/src/user_action/action_reorder_object.cpp b/plugins/gui/src/user_action/action_reorder_object.cpp index d3f915fb716..120aedb02d4 100644 --- a/plugins/gui/src/user_action/action_reorder_object.cpp +++ b/plugins/gui/src/user_action/action_reorder_object.cpp @@ -44,8 +44,7 @@ namespace hal if (pinGroup->size() > 1) { oldIndex = pin->get_group().second; - auto result = mod->move_pin_within_group(pinGroup, pin, mNewIndex); - if (result.is_error()) + if (!mod->move_pin_within_group(pinGroup, pin, mNewIndex)) { return false; } diff --git a/src/netlist/decorators/netlist_modification_decorator.cpp b/src/netlist/decorators/netlist_modification_decorator.cpp index 4eee34294d3..1b37bd13f17 100644 --- a/src/netlist/decorators/netlist_modification_decorator.cpp +++ b/src/netlist/decorators/netlist_modification_decorator.cpp @@ -297,10 +297,9 @@ namespace hal // delete old pin group incase it is now empty if (current_pin_group->get_pins().empty()) { - if (const auto res = m->delete_pin_group(current_pin_group); !res.is_ok()) + if (!m->delete_pin_group(current_pin_group)) { - return ERR_APPEND(res.get_error(), - "could not connect master net '" + master_net->get_name() + "' with ID " + std::to_string(master_net->get_id()) + " with slave net '" + slave_net->get_name() + return ERR("could not connect master net '" + master_net->get_name() + "' with ID " + std::to_string(master_net->get_id()) + " with slave net '" + slave_net->get_name() + "' with ID " + std::to_string(slave_net->get_id()) + ": failed to delete pingroup " + current_pin_group->get_name() + "with ID " + std::to_string(current_pin_group->get_id()) + " that is now empty."); } @@ -346,4 +345,4 @@ namespace hal return OK(master_net); } -} // namespace hal \ No newline at end of file +} // namespace hal diff --git a/src/netlist/gate_library/gate_type.cpp b/src/netlist/gate_library/gate_type.cpp index 2d569e30e8d..c8e2d2672ff 100644 --- a/src/netlist/gate_library/gate_type.cpp +++ b/src/netlist/gate_library/gate_type.cpp @@ -383,7 +383,7 @@ namespace hal { if (auto res = assign_pin_to_group(pin_group, pin, delete_empty_groups); res.is_error()) { - assert(delete_pin_group(pin_group).is_ok()); + assert(delete_pin_group(pin_group)); return ERR(res.get_error()); } } @@ -397,17 +397,17 @@ namespace hal return create_pin_group(get_unique_pin_group_id(), name, pins, direction, type, ascending, start_index, delete_empty_groups); } - Result GateType::delete_pin_group_internal(PinGroup* pin_group) + bool GateType::delete_pin_group_internal(PinGroup* pin_group) { // some sanity checks if (pin_group == nullptr) { - return ERR("could not delete pin group of gate type '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group is a 'nullptr'"); + log_warning("gate", "could not delete pin group of gate type '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); + return false; } if (const auto it = m_pin_groups_map.find(pin_group->get_id()); it == m_pin_groups_map.end() || it->second != pin_group) { - return ERR("could not delete pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " of gate type '" + m_name + "' with ID " + std::to_string(m_id) - + ": pin group does not belong to gate type"); + log_warning("gate", "could not delete pin group '{}' with ID {} of gate type '{}' with ID {}: pin group does not belong to gate type", pin_group->get_name(), pin_group->get_id(), m_name, m_id); } // erase pin group @@ -422,20 +422,21 @@ namespace hal m_free_pin_group_ids.insert(del_id); m_used_pin_group_ids.erase(del_id); - return OK({}); + return true; } - Result GateType::delete_pin_group(PinGroup* pin_group) + bool GateType::delete_pin_group(PinGroup* pin_group) { if (pin_group == nullptr) { - return ERR("could not delete pin group from gate type '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group is a 'nullptr'"); + log_warning("gate", "could not delete pin group from gate type '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); + return false; } if (const auto it = m_pin_groups_map.find(pin_group->get_id()); it == m_pin_groups_map.end() || it->second != pin_group) { - return ERR("could not delete pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " from gate type '" + m_name + "' with ID " + std::to_string(m_id) - + ": pin group does not belong to gate type"); + log_warning("gate", "could not delete pin group '{}' with ID {} from gate type '{}' with ID {}: pin group does not belong to gate type", pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } bool removed_pins = false; @@ -446,16 +447,17 @@ namespace hal removed_pins = true; if (auto res = create_pin_group(pin->get_name(), {pin}, pin->get_direction(), pin->get_type(), true, 0, false); res.is_error()) { - return ERR(res.get_error()); + log_warning("gate", "{}", res.get_error().get()); + return false; } } - if (auto res = delete_pin_group_internal(pin_group); res.is_error()) + if (!delete_pin_group_internal(pin_group)) { - return ERR(res.get_error()); + return false; } - return OK({}); + return true; } Result GateType::assign_pin_to_group(PinGroup* pin_group, GatePin* pin, bool delete_empty_groups) @@ -496,10 +498,9 @@ namespace hal if (delete_empty_groups && pg->empty()) { - if (auto res = delete_pin_group_internal(pg); res.is_error()) + if (!delete_pin_group_internal(pg)) { - return ERR_APPEND(res.get_error(), - "could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " + return ERR("could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " of gate type '" + m_name + "' with ID " + std::to_string(m_id) + ": unable to delete pin group '" + pg->get_name() + "' with ID " + std::to_string(pg->get_id())); } diff --git a/src/netlist/module.cpp b/src/netlist/module.cpp index 4bab800c4d8..435f4856423 100644 --- a/src/netlist/module.cpp +++ b/src/netlist/module.cpp @@ -734,9 +734,9 @@ namespace hal { m_output_nets.erase(net); } - if (auto res = remove_pin_net(net); res.is_error()) + if (!remove_pin_net(net)) { - return res; + return ERR("Remove pin net failed"); } } } @@ -836,7 +836,7 @@ namespace hal { if (const auto group_res = create_pin_group_internal(get_unique_pin_group_id(), name, direction, type, true, 0); group_res.is_error()) { - assert(delete_pin_internal(pin_res.get()).is_ok()); + assert(delete_pin_internal(pin_res.get())); return ERR_APPEND(group_res.get_error(), "could not create pin '" + name + "' for module '" + m_name + "' with ID " + std::to_string(m_id) + ": failed to create pin group"); } else @@ -844,8 +844,8 @@ namespace hal // create_pin_group_internal OK if (const auto assign_res = group_res.get()->assign_pin(pin_res.get()); assign_res.is_error()) { - assert(delete_pin_internal(pin_res.get()).is_ok()); - assert(delete_pin_group_internal(group_res.get()).is_ok()); + assert(delete_pin_internal(pin_res.get())); + assert(delete_pin_group_internal(group_res.get())); return ERR_APPEND(assign_res.get_error(), "could not create pin '" + name + "' for module '" + m_name + "' with ID " + std::to_string(m_id) + ": failed to assign pin to pin group"); } @@ -1253,10 +1253,10 @@ namespace hal for (auto* pin : pins) { - if (auto res = assign_pin_to_group(pin_group, pin, delete_empty_groups); res.is_error()) + if (!assign_pin_to_group(pin_group, pin, delete_empty_groups)) { - assert(delete_pin_group(pin_group).is_ok()); - return ERR(res.get_error()); + assert(delete_pin_group(pin_group)); + return ERR("Assign pin to group failed."); } } @@ -1270,17 +1270,20 @@ namespace hal return create_pin_group(get_unique_pin_group_id(), name, pins, direction, type, ascending, start_index, delete_empty_groups); } - Result Module::delete_pin_group(PinGroup* pin_group) + bool Module::delete_pin_group(PinGroup* pin_group) { if (pin_group == nullptr) { - return ERR("could not delete pin group from module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group is a 'nullptr'"); + log_warning("module", "could not delete pin group from module '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); + return false; } if (const auto it = m_pin_groups_map.find(pin_group->get_id()); it == m_pin_groups_map.end() || it->second != pin_group) { - return ERR("could not delete pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " from module '" + m_name + "' with ID " + std::to_string(m_id) - + ": pin group does not belong to module"); + log_warning("module", + "could not delete pin group '{}' with ID {} from module '{}' with ID {}: pin group does not belong to module", + pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } bool removed_pins = false; @@ -1291,41 +1294,44 @@ namespace hal removed_pins = true; if (auto res = create_pin_group(pin->get_name(), {pin}, pin->get_direction(), pin->get_type(), true, 0, false); res.is_error()) { - return ERR(res.get_error()); + return false; } } u32 pin_group_id_to_delete = pin_group->get_id(); - if (auto res = delete_pin_group_internal(pin_group); res.is_error()) + if (!delete_pin_group_internal(pin_group)) { - return ERR(res.get_error()); + return false; } if (removed_pins) { m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupDelete,pin_group_id_to_delete)); } - return OK({}); + return true; } - Result Module::move_pin_group(PinGroup* pin_group, u32 new_index) + bool Module::move_pin_group(PinGroup* pin_group, u32 new_index) { if (pin_group == nullptr) { - return ERR("could not move pin group of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group is a 'nullptr'"); + log_warning("module", "could not move pin group of module '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); + return false; } if (const auto it = m_pin_groups_map.find(pin_group->get_id()); it == m_pin_groups_map.end() || it->second != pin_group) { - return ERR("could not move pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " within module '" + m_name + "' with ID " + std::to_string(m_id) - + ": pin group does not belong to module"); + log_warning("module", "could not move pin group '{}' with ID {} within module '{}' with ID {}: pin group does not belong to module", + pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (new_index >= m_pin_groups_ordered.size()) { - return ERR("could not move pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) - + ": index " + std::to_string(new_index) + " is out of bounds"); + log_warning("module", "could not move pin group '{}' with ID {} of module '{}' with ID {}: index {} is out of bounds", + pin_group->get_name(), pin_group->get_id(), m_name, m_id, new_index); + return false; } auto src_it = std::find(m_pin_groups_ordered.begin(), m_pin_groups_ordered.end(), pin_group); @@ -1333,7 +1339,7 @@ namespace hal std::advance(dst_it, new_index); if (src_it == dst_it) { - return OK({}); + return true; } else if (std::distance(m_pin_groups_ordered.begin(), src_it) < std::distance(m_pin_groups_ordered.begin(), dst_it)) { @@ -1346,32 +1352,36 @@ namespace hal } m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupReorder,pin_group->get_id())); - return OK({}); + return true; } - Result Module::assign_pin_to_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups) + bool Module::assign_pin_to_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups) { if (pin_group == nullptr) { - return ERR("could not assign pin to pin group of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group is a 'nullptr'"); + log_warning("module", "could not assign pin to pin group of module '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); + return false; } if (pin == nullptr) { - return ERR("could not assign pin to pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " - + std::to_string(m_id) + ": pin is a 'nullptr'"); + log_warning("module", "could not assign pin to pin group '{}' with ID {} of module '{}' with ID {}: pin is a 'nullptr'", + pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (const auto it = m_pin_groups_map.find(pin_group->get_id()); it == m_pin_groups_map.end() || it->second != pin_group) { - return ERR("could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group does not belong to module"); + log_warning("module", "could not assign pin '{}' with ID {} to pin group '{}' with ID {} of module '{}' with ID {}: pin group does not belong to module", + pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (const auto it = m_pins_map.find(pin->get_id()); it == m_pins_map.end() || it->second != pin) { - return ERR("could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin does not belong to module"); + log_warning("module", "could not assign pin '{}' with ID {} to pin group '{}' with ID {} of module '{}' with ID {}: pin does not belong to module", + pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (PinGroup* pg = pin->get_group().first; pg != nullptr) @@ -1379,104 +1389,111 @@ namespace hal // remove from old group and potentially delete old group if empty if (auto res = pg->remove_pin(pin); res.is_error()) { - return ERR_APPEND(res.get_error(), - "could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) + ": unable to remove pin from pin group '" + pg->get_name() - + "' with ID " + std::to_string(pg->get_id())); + log_warning("module", + "could not assign pin '{}' with ID {} to pin group '{}' with ID {} of module '{}' with ID {}: unable to remove pin from pin group '{}' with ID {}", + pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id, pg->get_name(), pg->get_id()); + return false; } if (delete_empty_groups && pg->empty()) { - if (auto res = delete_pin_group_internal(pg); res.is_error()) + if (!delete_pin_group_internal(pg)) { - return ERR_APPEND(res.get_error(), - "could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) + ": unable to delete pin group '" + pg->get_name() - + "' with ID " + std::to_string(pg->get_id())); + log_warning("module", + "could not assign pin '{}' with ID {} to pin group '{}' with ID {} of module '{}' with ID {}: unable to delete pin group '{}' with ID {}", + pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id, pg->get_name(), pg->get_id()); + return false; } } } if (auto res = pin_group->assign_pin(pin); res.is_error()) { - return ERR_APPEND(res.get_error(), - "could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id)); + log_warning("module", + "could not assign pin '{}' with ID {} to pin group '{}' with ID {} of module '{}' with ID {}", pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupPinAssign,pin_group->get_id())); - return OK({}); + return true;; } - Result Module::move_pin_within_group(PinGroup* pin_group, ModulePin* pin, u32 new_index) + bool Module::move_pin_within_group(PinGroup* pin_group, ModulePin* pin, u32 new_index) { if (pin_group == nullptr) { - return ERR("could not move pin within pin group of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group is a 'nullptr'"); + log_warning("module", "could not move pin within pin group of module '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); + return false; } if (pin == nullptr) { - return ERR("could not move pin within pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " - + std::to_string(m_id) + ": pin is a 'nullptr'"); + log_warning("module", "could not move pin within pin group '{}' with ID {} of module '{}' with ID {}: pin is a 'nullptr'", + pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (const auto it = m_pin_groups_map.find(pin_group->get_id()); it == m_pin_groups_map.end() || it->second != pin_group) { - return ERR("could not move pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " within pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group does not belong to module"); + log_warning("module", + "could not move pin '{}' with ID {} within pin group '{}' with ID {} of module '{}' with ID {}: pin group does not belong to module", + pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (const auto it = m_pins_map.find(pin->get_id()); it == m_pins_map.end() || it->second != pin) { - return ERR("could not move pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " within pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin does not belong to module"); + log_warning("module", + "could not move pin '{}' with ID {} within pin group '{}' with return ERRID {} of module '{}' with ID {}: pin does not belong to module", + pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (auto res = pin_group->move_pin(pin, new_index); res.is_error()) { - return ERR_APPEND(res.get_error(), - "could not move pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " within pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id)); + log_warning("module", + "could not move pin '{}' with ID {} within pin group '{}' with ID {} of module '{}' with ID {}", + pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinReorder,pin->get_id())); - return OK({}); + return true; } - Result Module::remove_pin_from_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups) + bool Module::remove_pin_from_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups) { if (pin_group == nullptr) { - return ERR("could not remove pin from pin group of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group is a 'nullptr'"); + log_warning("module", "could not remove pin from pin group of module '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); + return false; } if (pin == nullptr) { - return ERR("could not remove pin from pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " - + std::to_string(m_id) + ": pin is a 'nullptr'"); + log_warning("module", "could not remove pin from pin group '{}' with ID {} of module '{}' with ID {}: pin is a 'nullptr'", pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (const auto it = m_pin_groups_map.find(pin_group->get_id()); it == m_pin_groups_map.end() || it->second != pin_group) { - return ERR("could not remove pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " from pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group does not belong to module"); + log_warning("module", "could not remove pin '{}' with ID {} from pin group '{}' with ID {} of module '{}' with ID {}: pin group does not belong to module", pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (const auto it = m_pins_map.find(pin->get_id()); it == m_pins_map.end() || it->second != pin) { - return ERR("could not remove pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " from pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin does not belong to module"); + log_warning("module", "could not remove pin '{}' with ID {} from pin group '{}' with ID {} of module '{}' with ID {}: pin does not belong to module", pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } if (auto res = create_pin_group(get_unique_pin_group_id(), pin->get_name(), {pin}, pin->get_direction(), pin->get_type(), true, 0, delete_empty_groups); res.is_error()) { - return ERR_APPEND(res.get_error(), - "could not remove pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " from pin group '" + pin_group->get_name() + "' with ID " - + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) + ": unable to create new pin group for pin"); + log_warning("module", "could not remove pin '{}' with ID {} from pin group '{}' with ID {} of module '{}' with ID : unable to create new pin group for pin", pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } - return OK({}); + return true; } Result Module::assign_pin_net(const u32 pin_id, Net* net, PinDirection direction, const std::string& name, PinType type) @@ -1539,12 +1556,13 @@ namespace hal return OK(pin); } - Result Module::remove_pin_net(Net* net) + bool Module::remove_pin_net(Net* net) { auto pin = get_pin_by_net(net); if (pin == nullptr) { - return ERR("could not remove pin from net: failed to get pin corresponding to net"); + log_warning("module", "could not remove pin from net: failed to get pin corresponding to net"); + return false; } PinGroup* pin_group = pin->get_group().first; @@ -1552,32 +1570,29 @@ namespace hal if (auto res = pin_group->remove_pin(pin); res.is_error()) { - return ERR_APPEND(res.get_error(), - "could not remove pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " from net '" + net->get_name() + "' with ID " + std::to_string(net->get_id()) - + ": failed to remove pin from pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id())); + log_warning("module", "could not remove pin '{}' with ID {} from net '{}' with ID {}: failed to remove pin from pin group '{}' with ID {}", pin->get_name(), pin->get_id(), net->get_name(), net->get_id(), pin_group->get_name(), pin_group->get_id()); + return false; } if (pin_group->empty()) { - if (auto res = delete_pin_group_internal(pin_group); res.is_error()) + if (!delete_pin_group_internal(pin_group)) { - return ERR_APPEND(res.get_error(), - "could not remove pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " from net '" + net->get_name() + "' with ID " - + std::to_string(net->get_id()) + ": failed to delete pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id())); + log_warning("module", "could not remove pin '{}' with ID {} from net '{}' with ID {}: failed to delete pin group '{}' with ID {}", pin->get_name(), pin->get_id(), net->get_name(), net->get_id(), pin_group->get_name(), pin_group->get_id()); + return false; } } u32 pin_id_to_delete = pin->get_id(); - if (const auto res = delete_pin_internal(pin); res.is_error()) + if (!delete_pin_internal(pin)) { - return ERR_APPEND(res.get_error(), - "could not remove pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " from net '" + net->get_name() + "' with ID " + std::to_string(net->get_id()) - + ": failed to delete pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id())); + log_warning("module", "could not remove pin '{}' with ID {} from net '{}' with ID {}: failed to delete pin '{}' with ID {}", pin->get_name(), pin->get_id(), net->get_name(), net->get_id(), pin->get_name(), pin->get_id()); + return false; } m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinDelete,pin_id_to_delete)); - return OK({}); + return true; } Result Module::create_pin_internal(const u32 id, const std::string& name, Net* net, PinDirection direction, PinType type) @@ -1621,17 +1636,18 @@ namespace hal return OK(pin); } - Result Module::delete_pin_internal(ModulePin* pin) + bool Module::delete_pin_internal(ModulePin* pin) { // some sanity checks if (pin == nullptr) { - return ERR("could not delete pin of gate type '" + m_name + "' with ID " + std::to_string(m_id) + ": pin is a 'nullptr'"); + log_warning("module", "could not delete pin of gate type '{}' with ID {}: pin is a 'nullptr'", m_name, m_id); + return false; } if (const auto it = m_pins_map.find(pin->get_id()); it == m_pins_map.end() || it->second != pin) { - return ERR("could not delete pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) - + ": pin does not belong to module"); + log_warning("module", "could not delete pin '{}' with ID {} of module '{}' with ID {}: pin does not belong to module", pin->get_name(), pin->get_id(), m_name, m_id); + return false; } // erase pin @@ -1645,7 +1661,7 @@ namespace hal m_free_pin_ids.insert(del_id); m_used_pin_ids.erase(del_id); - return OK({}); + return true; } Result*> Module::create_pin_group_internal(const u32 id, const std::string& name, PinDirection direction, PinType type, bool ascending, u32 start_index) @@ -1682,17 +1698,20 @@ namespace hal return OK(pin_group); } - Result Module::delete_pin_group_internal(PinGroup* pin_group) + bool Module::delete_pin_group_internal(PinGroup* pin_group) { // some sanity checks if (pin_group == nullptr) { - return ERR("could not delete pin group of module '" + m_name + "' with ID " + std::to_string(m_id) + ": pin group is a 'nullptr'"); + log_warning("module", "could not delete pin group of module '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); + return false; } if (const auto it = m_pin_groups_map.find(pin_group->get_id()); it == m_pin_groups_map.end() || it->second != pin_group) { - return ERR("could not delete pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " of module '" + m_name + "' with ID " + std::to_string(m_id) - + ": pin group does not belong to module"); + log_warning("module", + "could not delete pin group '{}' with ID {} of module '{}' with ID {}: pin group does not belong to module", + pin_group->get_name(), pin_group->get_id(), m_name, m_id); + return false; } // erase pin group @@ -1707,7 +1726,7 @@ namespace hal m_free_pin_group_ids.insert(del_id); m_used_pin_group_ids.erase(del_id); - return OK({}); + return true; } u32 Module::pinevent_associated_data(PinEvent pev, u32 id) diff --git a/src/python_bindings/bindings/gate_type.cpp b/src/python_bindings/bindings/gate_type.cpp index 449940d128f..4875ab91245 100644 --- a/src/python_bindings/bindings/gate_type.cpp +++ b/src/python_bindings/bindings/gate_type.cpp @@ -487,14 +487,13 @@ namespace hal py_gate_type.def( "delete_pin_group", [](GateType& self, PinGroup* pin_group) { - auto res = self.delete_pin_group(pin_group); - if (res.is_ok()) + if (self.delete_pin_group(pin_group)) { return true; } else { - log_error("python_context", "error encountered while deleting pin group:\n{}", res.get_error().get()); + log_error("python_context", "error encountered while deleting pin group"); return false; } }, diff --git a/src/python_bindings/bindings/module.cpp b/src/python_bindings/bindings/module.cpp index 101d2047fe7..9c5f25dafa3 100644 --- a/src/python_bindings/bindings/module.cpp +++ b/src/python_bindings/bindings/module.cpp @@ -770,14 +770,13 @@ namespace hal py_module.def( "delete_pin_group", [](Module& self, PinGroup* pin_group) { - auto res = self.delete_pin_group(pin_group); - if (res.is_ok()) + if (self.delete_pin_group(pin_group)) { return true; } else { - log_error("python_context", "error encountered while deleting pin group:\n{}", res.get_error().get()); + log_error("python_context", "error encountered while deleting pin group."); return false; } }, @@ -793,14 +792,13 @@ namespace hal py_module.def( "move_pin_group", [](Module& self, PinGroup* pin_group, u32 new_index) { - auto res = self.move_pin_group(pin_group, new_index); - if (res.is_ok()) + if (self.move_pin_group(pin_group, new_index)) { return true; } else { - log_error("python_context", "error encountered while moving pin group:\n{}", res.get_error().get()); + log_error("python_context", "error encountered while moving pin group."); return false; } }, @@ -819,14 +817,13 @@ namespace hal py_module.def( "assign_pin_to_group", [](Module& self, PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups = true) { - auto res = self.assign_pin_to_group(pin_group, pin, delete_empty_groups); - if (res.is_ok()) + if (self.assign_pin_to_group(pin_group, pin, delete_empty_groups)) { return true; } else { - log_error("python_context", "error encountered while assigning pin to pin group:\n{}", res.get_error().get()); + log_error("python_context", "error encountered while assigning pin to pin group."); return false; } }, @@ -846,14 +843,13 @@ namespace hal py_module.def( "move_pin_within_group", [](Module& self, PinGroup* pin_group, ModulePin* pin, u32 new_index) { - auto res = self.move_pin_within_group(pin_group, pin, new_index); - if (res.is_ok()) + if (self.move_pin_within_group(pin_group, pin, new_index)) { return true; } else { - log_error("python_context", "error encountered while moving pin within pin group:\n{}", res.get_error().get()); + log_error("python_context", "error encountered while moving pin within pin group."); return false; } }, @@ -874,14 +870,13 @@ namespace hal py_module.def( "remove_pin_from_group", [](Module& self, PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups = true) { - auto res = self.remove_pin_from_group(pin_group, pin, delete_empty_groups); - if (res.is_ok()) + if (self.remove_pin_from_group(pin_group, pin, delete_empty_groups)) { return true; } else { - log_error("python_context", "error encountered while removing pin from pin group:\n{}", res.get_error().get()); + log_error("python_context", "error encountered while removing pin from pin group."); return false; } }, diff --git a/tests/netlist/module.cpp b/tests/netlist/module.cpp index 20a2d552561..e8dd56823e4 100644 --- a/tests/netlist/module.cpp +++ b/tests/netlist/module.cpp @@ -980,7 +980,7 @@ namespace hal { // delete pin group u32 group_id = in_group->get_id(); - EXPECT_TRUE(m_1->delete_pin_group(in_group).is_ok()); + EXPECT_TRUE(m_1->delete_pin_group(in_group)); EXPECT_EQ(m_1->get_pin_group_by_id(group_id), nullptr); EXPECT_EQ(m_1->get_pin_groups().size(), 4); PinGroup* in_group_0 = in_pin_0->get_group().first; @@ -995,7 +995,7 @@ namespace hal { EXPECT_EQ(in_group_0->get_index(in_pin_0).get(), 0); // assign pin to group - EXPECT_TRUE(m_1->assign_pin_to_group(in_group_0, in_pin_1).is_ok()); + EXPECT_TRUE(m_1->assign_pin_to_group(in_group_0, in_pin_1)); EXPECT_EQ(m_1->get_pin_groups().size(), 3); EXPECT_EQ(in_group_0->size(), 2); EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group_0, i32(0))); @@ -1006,7 +1006,7 @@ namespace hal { EXPECT_EQ(in_group_0->get_pin_at_index(1).get(), in_pin_1); // move pins within group - EXPECT_TRUE(m_1->move_pin_within_group(in_group_0, in_pin_1, 0).is_ok()); + EXPECT_TRUE(m_1->move_pin_within_group(in_group_0, in_pin_1, 0)); EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group_0, i32(1))); EXPECT_EQ(in_group_0->get_index(in_pin_0).get(), 1); EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group_0, i32(0))); @@ -1014,7 +1014,7 @@ namespace hal { EXPECT_EQ(in_group_0->get_pin_at_index(1).get(), in_pin_0); EXPECT_EQ(in_group_0->get_pin_at_index(0).get(), in_pin_1); - EXPECT_TRUE(m_1->move_pin_within_group(in_group_0, in_pin_1, 1).is_ok()); + EXPECT_TRUE(m_1->move_pin_within_group(in_group_0, in_pin_1, 1)); EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group_0, i32(0))); EXPECT_EQ(in_group_0->get_index(in_pin_0).get(), 0); EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group_0, i32(1))); @@ -1024,20 +1024,20 @@ namespace hal { // remove pin from group EXPECT_TRUE(m_1->set_pin_group_name(in_group_0, "I_tmp")); - EXPECT_TRUE(m_1->remove_pin_from_group(in_group_0, in_pin_0).is_ok()); + EXPECT_TRUE(m_1->remove_pin_from_group(in_group_0, in_pin_0)); EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group_0, i32(0))); EXPECT_EQ(in_group_0->get_index(in_pin_1).get(), 0); EXPECT_EQ(in_group_0->get_pin_at_index(0).get(), in_pin_1); EXPECT_EQ(in_pin_0->get_group().first->get_name(), in_pin_0->get_name()); EXPECT_EQ(in_pin_0->get_group().second, 0); - EXPECT_TRUE(m_1->assign_pin_to_group(in_group_0, in_pin_0).is_ok()); + EXPECT_TRUE(m_1->assign_pin_to_group(in_group_0, in_pin_0)); EXPECT_EQ(in_group_0->size(), 2); // try mixed directions and types EXPECT_TRUE(m_1->set_pin_type(out_pin_0, PinType::address)); EXPECT_TRUE(m_1->set_pin_type(out_pin_1, PinType::data)); - EXPECT_TRUE(m_1->assign_pin_to_group(in_group_0, out_pin_0).is_ok()); // wrong direction - EXPECT_TRUE(m_1->assign_pin_to_group(in_group_0, out_pin_1).is_ok()); // wrong type + EXPECT_TRUE(m_1->assign_pin_to_group(in_group_0, out_pin_0)); // wrong direction + EXPECT_TRUE(m_1->assign_pin_to_group(in_group_0, out_pin_1)); // wrong type EXPECT_EQ(in_group_0->size(), 4); EXPECT_TRUE(m_1->create_pin_group("O1", {out_pin_0, out_pin_1}).is_ok()); // different types EXPECT_TRUE(m_1->create_pin_group("O2", {out_pin_0, in_pin_0}).is_ok()); // different directions @@ -1137,7 +1137,7 @@ namespace hal { EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(1))); // move pins within group - EXPECT_TRUE(m_1->move_pin_within_group(in_group, in_pin_1, 2).is_ok()); + EXPECT_TRUE(m_1->move_pin_within_group(in_group, in_pin_1, 2)); EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group, i32(1))); EXPECT_EQ(in_group->get_index(in_pin_0).get(), 1); EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(2))); @@ -1145,7 +1145,7 @@ namespace hal { EXPECT_EQ(in_group->get_pin_at_index(1).get(), in_pin_0); EXPECT_EQ(in_group->get_pin_at_index(2).get(), in_pin_1); - EXPECT_TRUE(m_1->move_pin_within_group(in_group, in_pin_1, 1).is_ok()); + EXPECT_TRUE(m_1->move_pin_within_group(in_group, in_pin_1, 1)); EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group, i32(2))); EXPECT_EQ(in_group->get_index(in_pin_0).get(), 2); EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(1))); @@ -1154,17 +1154,17 @@ namespace hal { EXPECT_EQ(in_group->get_pin_at_index(1).get(), in_pin_1); // remove pin from group - EXPECT_TRUE(m_1->remove_pin_from_group(in_group, in_pin_0).is_ok()); + EXPECT_TRUE(m_1->remove_pin_from_group(in_group, in_pin_0)); EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(2))); EXPECT_EQ(in_group->get_index(in_pin_1).get(), 2); EXPECT_EQ(in_group->get_pin_at_index(2).get(), in_pin_1); EXPECT_EQ(in_pin_0->get_group().first->get_name(), in_pin_0->get_name()); EXPECT_EQ(in_pin_0->get_group().second, 0); - EXPECT_TRUE(m_1->assign_pin_to_group(in_group, in_pin_0).is_ok()); + EXPECT_TRUE(m_1->assign_pin_to_group(in_group, in_pin_0)); EXPECT_EQ(in_group->size(), 2); // assign pin to group - EXPECT_TRUE(m_1->assign_pin_to_group(in_group, in_pin_0).is_ok()); + EXPECT_TRUE(m_1->assign_pin_to_group(in_group, in_pin_0)); EXPECT_EQ(m_1->get_pin_groups().size(), 3); EXPECT_EQ(in_group->size(), 2); EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group, i32(1))); @@ -1268,7 +1268,7 @@ namespace hal { // Create the listener for the tested event test_utils::EventListener listener; std::function cb = listener.get_conditional_callback( - [=](ModuleEvent::event ev, Module* m, u32 id){return ev == event_type[event_idx] && m == test_mod;} + [=](ModuleEvent::event ev, Module* m, u32 /*id*/){return ev == event_type[event_idx] && m == test_mod;} ); std::string cb_name = "mod_event_callback_" + std::to_string((u32)event_type[event_idx]); // Register a callback of the listener @@ -1288,7 +1288,7 @@ namespace hal { // -- 'created' event test_utils::EventListener listener_created; std::function cb_created = listener_created.get_conditional_callback( - [=](ModuleEvent::event ev, Module* m, u32 id){return ev == ModuleEvent::event::created;} + [=](ModuleEvent::event ev, Module* /*m*/, u32 /*id*/){return ev == ModuleEvent::event::created;} ); std::string cb_name_created = "mod_event_callback_created"; test_nl->get_event_handler()->register_callback(cb_name_created, cb_created); @@ -1303,7 +1303,7 @@ namespace hal { // -- 'removed' event test_utils::EventListener listener_removed; std::function cb_removed = listener_removed.get_conditional_callback( - [=](ModuleEvent::event ev, Module* m, u32 id){return ev == ModuleEvent::event::removed;} + [=](ModuleEvent::event ev, Module* /*m*/, u32 /*id*/){return ev == ModuleEvent::event::removed;} ); std::string cb_name_removed = "mod_event_callback_removed"; test_nl->get_event_handler()->register_callback(cb_name_removed, cb_removed); From a90e15a458ccfba99dade401f5b002e762fb9869 Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 18 Sep 2023 14:37:03 +0200 Subject: [PATCH 12/66] Start handling of parameter for pin_changed core event --- .../netlist/gate_library/enums/pin_type.h | 2 ++ .../module_details_widget/port_tree_model.cpp | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/hal_core/netlist/gate_library/enums/pin_type.h b/include/hal_core/netlist/gate_library/enums/pin_type.h index ec073a7768b..63f468c5834 100644 --- a/include/hal_core/netlist/gate_library/enums/pin_type.h +++ b/include/hal_core/netlist/gate_library/enums/pin_type.h @@ -62,12 +62,14 @@ namespace hal GroupReorder, GroupRename, GroupTypeChange, + GroupDirChange, GroupPinAssign, GroupDelete, PinCreate, PinReorder, PinRename, PinTypeChange, + PinDirChange, PinDelete }; } // namespace hal diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index 84e15af7392..67964b67636 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -525,11 +525,17 @@ namespace hal { Q_UNUSED(pev); Q_UNUSED(pgid); - if ((int)m->get_id() == mModuleId) - { - if (!mIgnoreEventsFlag) - setModule(m); - } + if ((int)m->get_id() != mModuleId) return; + + if (!mIgnoreEventsFlag) + setModule(m); + + PortTreeItem* pti = nullptr; + + if (pev < PinEvent::PinCreate) + pti = dynamic_cast(mIdToGroupItem.value(pgid)); + else + pti = dynamic_cast(mIdToPinItem.value(pgid)); } void ModulePinsTreeModel::dndGroupOnGroup(BaseTreeItem *droppedGroup, BaseTreeItem *onDroppedGroup) From b7f9bac67380838482de02057e3d3f39c35babd4 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 20 Sep 2023 21:48:55 +0200 Subject: [PATCH 13/66] Handling of individual pin_changed events in GUI (albeit not fully tested) --- .../netlist/gate_library/enums/pin_type.h | 2 +- .../module_details_widget/port_tree_model.h | 27 +- .../net_details_widget/module_table_model.h | 3 +- .../include/gui/user_action/action_pingroup.h | 2 +- .../module_ports_tree.cpp | 8 +- .../module_details_widget/port_tree_model.cpp | 230 +++++++++++++----- .../net_details_widget/module_table_model.cpp | 4 +- src/netlist/module.cpp | 12 +- 8 files changed, 205 insertions(+), 83 deletions(-) diff --git a/include/hal_core/netlist/gate_library/enums/pin_type.h b/include/hal_core/netlist/gate_library/enums/pin_type.h index 63f468c5834..6f8b660ad07 100644 --- a/include/hal_core/netlist/gate_library/enums/pin_type.h +++ b/include/hal_core/netlist/gate_library/enums/pin_type.h @@ -63,10 +63,10 @@ namespace hal GroupRename, GroupTypeChange, GroupDirChange, - GroupPinAssign, GroupDelete, PinCreate, PinReorder, + PinAssignToGroup, PinRename, PinTypeChange, PinDirChange, diff --git a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h index 45d5b02b91e..938f683caa0 100644 --- a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h @@ -28,6 +28,7 @@ //#include "gui/new_selection_details_widget/models/base_tree_model.h" #include "gui/basic_tree_model/base_tree_model.h" #include "hal_core/netlist/gate_library/enums/pin_type.h" +#include "hal_core/netlist/gate_library/enums/pin_direction.h" #include namespace hal @@ -42,24 +43,31 @@ namespace hal enum Type {None, Pin, Group}; private: - Type mType; + Type mItemType; u32 mId; QString mPinName; - QString mPinDirection; - QString mPinType; + PinDirection mPinDirection; + PinType mPinType; QString mNetName; + + void setPinType(const QString& type); + void setPinDirection(const QString& dir); public: - PortTreeItem(Type tp, QString pinName, QString pinDirection, QString pinType, QString netName); - PortTreeItem() : mType(None), mId(0) {;} + PortTreeItem(Type itype, QString pinName, PinDirection dir, PinType ptype, QString netName = QString()); + PortTreeItem() : mItemType(None), mId(0) {;} QVariant getData(int column) const override; void setData(QList data) override; void setDataAtIndex(int index, QVariant& data) override; void appendData(QVariant data) override; int getColumnCount() const override; - void setType(Type tp) { mType = tp; } - Type type() const { return mType; } + void setItemType(Type tp) { mItemType = tp; } + Type itemType() const { return mItemType; } void setId(u32 id_) { mId = id_; } + QString name() const { return mPinName; } + void setName(const QString& nam) { mPinName = nam; } + void setPinType(PinType ptype) { mPinType = ptype; } + void setPinDirection(PinDirection dir) { mPinDirection = dir; } /** * Returns the pin-id if the item represents a pin or the pingroup-id @@ -155,9 +163,8 @@ namespace hal Module* mModule; //name is (hopefully) enough to identify QMap mNameToTreeItem; - QMap mIdToPinItem; - QMap mIdToGroupItem; - bool mIgnoreEventsFlag; + QMap mIdToPinItem; + QMap mIdToGroupItem; void insertItem(PortTreeItem* item, BaseTreeItem* parent, int index); void removeItem(PortTreeItem* item); diff --git a/plugins/gui/include/gui/selection_details_widget/net_details_widget/module_table_model.h b/plugins/gui/include/gui/selection_details_widget/net_details_widget/module_table_model.h index a1b6a8cdf31..7749794da7e 100644 --- a/plugins/gui/include/gui/selection_details_widget/net_details_widget/module_table_model.h +++ b/plugins/gui/include/gui/selection_details_widget/net_details_widget/module_table_model.h @@ -26,6 +26,7 @@ #pragma once #include "hal_core/defines.h" +#include "hal_core/netlist/gate_library/enums/pin_type.h" #include #include @@ -154,7 +155,7 @@ class ModuleTableModel : public QAbstractTableModel /** @name Event Handler Functions */ ///@{ - void handleModulePortsChanged(Module* m); + void handleModulePortsChanged(Module* m, PinEvent pev, u32 pgid); void handleModuleRemoved(Module* m); ///@} diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index 26703e791e6..9c9c075129d 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -46,7 +46,7 @@ namespace hal static bool useExistingPin(Type tp); }; - int pinGroupIndex(const Module* mod, const PinGroup* pgrp); + int pinGroupIndex(const Module* mod, const PinGroup* pgrp); /** * @ingroup user_action diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp index 7b2c60d973e..cd605847317 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp @@ -140,7 +140,7 @@ namespace hal }); } - if (clickedItem->type() == PortTreeItem::Group) //group specific context, own helper function? (returns at the end) + if (clickedItem->itemType() == PortTreeItem::Group) //group specific context, own helper function? (returns at the end) { menu.addAction("Change name", [name, modId, itemId]() { InputDialog ipd("Change pin group name", "New group name", name); @@ -244,7 +244,7 @@ namespace hal appendMultiSelectionEntries(menu, modId); menu.addSection("Python"); - if(clickedItem->type()==PortTreeItem::Pin) + if(clickedItem->itemType()==PortTreeItem::Pin) menu.addAction(QIcon(":/icons/python"), "Get pin", [modId, itemId]() { QApplication::clipboard()->setText(PyCodeProvider::pyCodeModulePinById(modId, itemId)); }); else menu.addAction(QIcon(":/icons/python"), "Get group", [modId, itemId]() { QApplication::clipboard()->setText(PyCodeProvider::pyCodeModulePinGroup(modId, itemId)); }); @@ -297,7 +297,7 @@ namespace hal for (auto index : selectionModel()->selectedRows()) { PortTreeItem* item = static_cast(mPortModel->getItemFromIndex(index)); - if (item->type() == PortTreeItem::Pin) + if (item->itemType() == PortTreeItem::Pin) { if (!alreadyProcessedPins.contains(item)) { @@ -305,7 +305,7 @@ namespace hal alreadyProcessedPins.insert(item); } } - else if (item->type() == PortTreeItem::Group) + else if (item->itemType() == PortTreeItem::Group) { onlyPins = false; for (auto pinItem : item->getChildren()) diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index 67964b67636..edaa73f4917 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -19,38 +19,51 @@ namespace hal { - PortTreeItem::PortTreeItem(Type tp, QString pinName, QString pinDirection, QString pinTypee, QString netName) - : mType(tp), mPinName(pinName), mPinDirection(pinDirection), mPinType(pinTypee), mNetName(netName) + PortTreeItem::PortTreeItem(Type itype, QString pinName, PinDirection dir, PinType ptype, QString netName) + : mItemType(itype), mPinName(pinName), mPinDirection(dir), mPinType(ptype), mNetName(netName) {;} QVariant PortTreeItem::getData(int index) const { switch (index) { - case 0: { - QVariant qvPinName = QVariant(mPinName); - return qvPinName; - break;} - case 1: { - QVariant qvPinDirection = QVariant(mPinDirection); - return qvPinDirection; - break;} - case 2: { - QVariant qvPinType = QVariant(mPinType); - return qvPinType; - break;} - case 3: { - QVariant qvNetName = QVariant(mNetName); - return qvNetName; - break;} + case 0: + return mPinName; + case 1: + return QString::fromStdString(enum_to_string(mPinDirection)); + case 2: + return QString::fromStdString(enum_to_string(mPinType)); + case 3: + return mNetName; + } + return QVariant(); + } + + void PortTreeItem::setPinType(const QString& type) + { + mPinType = PinType::none; + for (int pt = 1; pt < (int) PinType::sum; ++pt) + { + if (QString::fromStdString(enum_to_string((PinType)pt)) == type) + mPinType = (PinType)pt; + } + } + + void PortTreeItem::setPinDirection(const QString& dir) + { + mPinDirection = PinDirection::none; + for (int pd = 1; pd <= (int) PinDirection::internal; ++pd) + { + if (QString::fromStdString(enum_to_string((PinDirection)pd)) == dir) + mPinDirection = (PinDirection)pd; } } void PortTreeItem::setData(QList data) { mPinName = data[0].toString(); - mPinDirection = data[1].toString(); - mPinType = data[2].toString(); + setPinDirection(data[1].toString()); + setPinType(data[2].toString()); mNetName = data[3].toString(); } @@ -62,10 +75,10 @@ namespace hal mPinName = data.toString(); break;} case 1: { - mPinDirection = data.toString(); + setPinDirection(data.toString()); break;} case 2: { - mPinType = data.toString(); + setPinType(data.toString()); break;} case 3: { mNetName = data.toString(); @@ -75,7 +88,7 @@ namespace hal void PortTreeItem::appendData(QVariant data) { - + Q_UNUSED(data) } int PortTreeItem::getColumnCount() const @@ -83,7 +96,7 @@ namespace hal return 4; } - ModulePinsTreeModel::ModulePinsTreeModel(QObject* parent) : BaseTreeModel(parent), mIgnoreEventsFlag(false) + ModulePinsTreeModel::ModulePinsTreeModel(QObject* parent) : BaseTreeModel(parent) { setHeaderLabels(QStringList() << "Name" << "Direction" @@ -146,7 +159,7 @@ namespace hal PortTreeItem* item = static_cast(getItemFromIndex(indexes.at(0))); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); - QString type = item->type() == PortTreeItem::Pin ? "pin" : "group"; + QString type = item->itemType() == PortTreeItem::Pin ? "pin" : "group"; stream << type << item->id(); data->setText(item->getData(sNameColumn).toString()); data->setData("pintreemodel/item", encodedData); @@ -249,7 +262,6 @@ namespace hal // return false; // removeItem(droppedItem); -// mIgnoreEventsFlag = true; // if (bottomEdge) // { // insertItem(newItem, droppedParentItem, row - 1); @@ -272,7 +284,6 @@ namespace hal // reorderObj->setParentObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); // reorderObj->exec(); // } -// mIgnoreEventsFlag = false; // return true; // } @@ -284,7 +295,6 @@ namespace hal // if (pinGroup == nullptr) // return false; -// mIgnoreEventsFlag = true; // UserActionCompound* comp = new UserActionCompound; // if (droppedParentItem != mRootItem && onDroppedParentItem != mRootItem) @@ -304,7 +314,6 @@ namespace hal // comp->addAction(addAct); // comp->addAction(reordAct); // bool ret = comp->exec(); -// mIgnoreEventsFlag = false; // if (ret) // { // removeItem(droppedItem); @@ -324,7 +333,6 @@ namespace hal // //if(droppedParentItem == mRootItem)//item which is dropped // if (droppedParentItem != onDroppedItem) // { -// mIgnoreEventsFlag = true; // //bool ret = mod->assign_pin_to_group(onDroppedGroup, droppedPin); // ActionAddItemsToObject* addAct = new ActionAddItemsToObject(QSet(), QSet(), QSet(), QSet() << droppedPin->get_id()); // addAct->setObject(UserActionObject(onDroppedGroup->get_id(), UserActionObjectType::PinGroup)); @@ -334,10 +342,8 @@ namespace hal // { // removeItem(droppedItem); // insertItem(newItem, onDroppedItem, onDroppedItem->getChildCount()); -// mIgnoreEventsFlag = false; // return true; // } -// mIgnoreEventsFlag = false; // return false; // } // } @@ -367,7 +373,7 @@ namespace hal auto item = static_cast(mIdToGroupItem[id]); if(parentItem) { - if(item->type() == PortTreeItem::Pin || (item->type() == PortTreeItem::Group && row != -1) || item == parentItem) + if(item->itemType() == PortTreeItem::Pin || (item->itemType() == PortTreeItem::Group && row != -1) || item == parentItem) return false; } else // here, only check for adjacent rows @@ -383,7 +389,7 @@ namespace hal // otherwise it does not make much sense...perhaps change this check auto item = mIdToPinItem[id]; if((!parentItem && item->getParent()->getChildCount() == 1) || (item->getParent() == parentItem && row == -1) || item == parentItem - || (parentItem && (parentItem->type() == PortTreeItem::Pin))) + || (parentItem && (parentItem->itemType() == PortTreeItem::Pin))) return false; // case if one wants to drop between pins in same group, check if its not adjacent row (other cases are handled on case above if(item->getParent() == parentItem) @@ -419,18 +425,15 @@ namespace hal continue; auto pinGroupName = QString::fromStdString(pinGroup->get_name()); - auto pinGroupDirection = QString::fromStdString(enum_to_string((pinGroup->get_direction()))); - auto pinGroupType = QString::fromStdString(enum_to_string(pinGroup->get_type())); - - PortTreeItem* pinGroupItem = new PortTreeItem(PortTreeItem::Group,pinGroupName, pinGroupDirection, pinGroupType, ""); + PortTreeItem* pinGroupItem = new PortTreeItem(PortTreeItem::Group,pinGroupName, pinGroup->get_direction(), pinGroup->get_type()); pinGroupItem->setId(pinGroup->get_id()); mIdToGroupItem.insert(pinGroup->get_id(), pinGroupItem); for(ModulePin* pin : pinGroup->get_pins()) { PortTreeItem* pinItem = new PortTreeItem(PortTreeItem::Pin, QString::fromStdString(pin->get_name()), - QString::fromStdString(enum_to_string(pin->get_direction())), - QString::fromStdString(enum_to_string(pin->get_type())), + pin->get_direction(), + pin->get_type(), QString::fromStdString(pin->get_net()->get_name())); pinItem->setId(pin->get_id()); pinGroupItem->appendChild(pinItem); @@ -500,7 +503,7 @@ namespace hal if (mModuleId == -1) //no current module = no represented net return nullptr; - if (item->type() == PortTreeItem::Group && item->getChildCount() > 1) + if (item->itemType() == PortTreeItem::Group && item->getChildCount() > 1) return nullptr; Module* m = gNetlist->get_module_by_id(mModuleId); @@ -527,15 +530,129 @@ namespace hal Q_UNUSED(pgid); if ((int)m->get_id() != mModuleId) return; - if (!mIgnoreEventsFlag) - setModule(m); - - PortTreeItem* pti = nullptr; + PortTreeItem* ptiPin = nullptr; + PortTreeItem* ptiGroup = nullptr; + const PinGroup* pgroup = nullptr; + const ModulePin* pin = nullptr; + int pinRow = -1; if (pev < PinEvent::PinCreate) - pti = dynamic_cast(mIdToGroupItem.value(pgid)); + { + // group event + ptiGroup = mIdToGroupItem.value(pgid); + pgroup = m->get_pin_group_by_id(pgid); + if (!pgroup) + { + log_warning("gui", "Cannot handle event for pin group ID={}, no such group.", pgid); + return; + } + } else - pti = dynamic_cast(mIdToPinItem.value(pgid)); + { + // pin event + ptiPin = mIdToPinItem.value(pgid); + pin = m->get_pin_by_id(pgid); + if (!pin) + { + log_warning("gui", "Cannot handle event for pin ID={}, no such pid.", pgid); + return; + } + auto pgPair = pin->get_group(); + pgroup = pgPair.first; + pinRow = pgPair.second; + if (pgroup) + ptiGroup = mIdToGroupItem.value(pgroup->get_id()); + } + + switch (pev) + { + case PinEvent::GroupCreate: + { + ptiGroup = new PortTreeItem(PortTreeItem::Group, + QString::fromStdString(pgroup->get_name()), + pgroup->get_direction(), + pgroup->get_type()); + int inx = pinGroupIndex(m,pgroup); + insertItem(ptiGroup, mRootItem, inx); + break; + } + case PinEvent::GroupReorder: + { + int inx = pinGroupIndex(m,pgroup); + removeItem(ptiGroup); + insertItem(ptiGroup, mRootItem, inx); + break; + } + case PinEvent::GroupRename: + ptiGroup->setName(QString::fromStdString(pgroup->get_name())); + break; + case PinEvent::GroupTypeChange: + ptiGroup->setPinType(pgroup->get_type()); + break; + case PinEvent::GroupDirChange: + ptiGroup->setPinDirection(pgroup->get_direction()); + break; + case PinEvent::GroupDelete: + removeItem(ptiGroup); + delete ptiGroup; + break; + case PinEvent::PinCreate: + { + if (!pgroup || !ptiGroup) + { + log_warning("gui", "Cannot handle pin create event for pin ID={}, pin is not assigned to any group.", pgid); + return; + } + QString netName; + if (pin->get_net()) + netName = QString::fromStdString(pin->get_net()->get_name()); + ptiPin = new PortTreeItem(PortTreeItem::Pin, + QString::fromStdString(pin->get_name()), + pin->get_direction(), + pin->get_type(), + netName); + + insertItem(ptiPin, ptiGroup, pinRow); + break; + } + case PinEvent::PinReorder: + { + if (!pgroup || !ptiGroup) + { + log_warning("gui", "Cannot handle pin reorder event for pin ID={}, pin is not assigned to any group.", pgid); + return; + } + removeItem(ptiPin); + insertItem(ptiPin, ptiGroup, pinRow); + break; + } + case PinEvent::PinAssignToGroup: + { + if (!pgroup || !ptiGroup) + { + log_warning("gui", "Cannot handle pin assign event for pin ID={}, pin is not assigned to any group.", pgid); + return; + } + removeItem(ptiPin); + insertItem(ptiPin, ptiGroup, pinRow); + break; + } + case PinEvent::PinRename: + ptiPin->setName(QString::fromStdString(pin->get_name())); + break; + case PinEvent::PinTypeChange: + ptiPin->setPinType(pin->get_type()); + break; + case PinEvent::PinDirChange: + ptiPin->setPinDirection(pin->get_direction()); + break; + case PinEvent::PinDelete: + removeItem(ptiPin); + delete ptiPin; + break; + default: + break; + } } void ModulePinsTreeModel::dndGroupOnGroup(BaseTreeItem *droppedGroup, BaseTreeItem *onDroppedGroup) @@ -544,7 +661,6 @@ namespace hal // 2) just add all pins from dropped group to "ondroppedgroup", then rename? // InputDialog ipd("Name of new group", "Name of new group:", onDroppedGroup->getData(sNameColumn).toString()); // if(ipd.exec() == QDialog::Rejected) return false; - mIgnoreEventsFlag = true; QList pins; auto srcgroup = mModule->get_pin_group_by_id(static_cast(droppedGroup)->id()); for(const auto &pin : srcgroup->get_pins()) @@ -558,14 +674,10 @@ namespace hal // too keep the order, ActionAddItemsToObject cannot be executed with all pins, but a ComAction must be created // with many ActionAddItemsToObject that contain a single pin each -> set destroys order of pins in source pingroup - setModule(mModule); - mIgnoreEventsFlag = false; - } void ModulePinsTreeModel::dndGroupBetweenGroup(PortTreeItem *droppedGroup, int row) { - mIgnoreEventsFlag = true; int ownRow = droppedGroup->getOwnRow(); bool bottomEdge = row == mRootItem->getChildCount(); auto desiredIdx = bottomEdge ? row-1 : row; @@ -577,13 +689,10 @@ namespace hal removeItem(droppedGroup); insertItem(droppedGroup, mRootItem, desiredIdx); } - mIgnoreEventsFlag = false; } void ModulePinsTreeModel::dndPinOnGroup(PortTreeItem *droppedPin, BaseTreeItem *onDroppedGroup) { - mIgnoreEventsFlag = true; - ActionPingroup* act = new ActionPingroup(PinActionType::PinAsignGroup,droppedPin->id(),"",static_cast(onDroppedGroup)->id()); act->exec(); auto oldParent = droppedPin->getParent(); @@ -593,13 +702,10 @@ namespace hal removeItem(static_cast(oldParent)); delete oldParent; } - //setModule(mModule); - mIgnoreEventsFlag = false; } void ModulePinsTreeModel::dndPinBetweenPin(PortTreeItem *droppedPin, BaseTreeItem *onDroppedParent, int row) { - mIgnoreEventsFlag = true; int desiredIdx = row; ActionPingroup* act = nullptr; if(droppedPin->getParent() == onDroppedParent) // same group @@ -624,15 +730,12 @@ namespace hal removeItem(static_cast(oldParent)); delete oldParent; } - //setModule(mModule); - mIgnoreEventsFlag = false; } void ModulePinsTreeModel::dndPinBetweenGroup(PortTreeItem *droppedPin, int row) { // row is needed for when groups can change its order within the module Q_UNUSED(row) - mIgnoreEventsFlag = true; auto pinToMove = mModule->get_pin_by_id(droppedPin->id()); if (!pinToMove) return; @@ -670,7 +773,6 @@ namespace hal insertItem(droppedPin, pinGroupItem, 0); } */ - mIgnoreEventsFlag = false; } void ModulePinsTreeModel::insertItem(PortTreeItem* item, BaseTreeItem* parent, int index) @@ -680,7 +782,7 @@ namespace hal parent->insertChild(index, item); endInsertRows(); //mNameToTreeItem.insert(item->getData(sNameColumn).toString(), item); - item->type() == PortTreeItem::Pin ? mIdToPinItem.insert(item->id(), item) : mIdToGroupItem.insert(item->id(), item); + item->itemType() == PortTreeItem::Pin ? mIdToPinItem.insert(item->id(), item) : mIdToGroupItem.insert(item->id(), item); //mIdToPinItem.insert(getIdOfItem(item), item); } void ModulePinsTreeModel::removeItem(PortTreeItem* item) @@ -690,7 +792,7 @@ namespace hal endRemoveRows(); //mNameToTreeItem.remove(item->getData(sNameColumn).toString()); //for now, only ids of pin-items (since these functions are only relevant for dnd) - item->type() == PortTreeItem::Pin ? mIdToPinItem.remove(item->id()) : mIdToGroupItem.remove(item->id()); + item->itemType() == PortTreeItem::Pin ? mIdToPinItem.remove(item->id()) : mIdToGroupItem.remove(item->id()); //mIdToPinItem.remove(getIdOfItem(item)); //delete item; } diff --git a/plugins/gui/src/selection_details_widget/net_details_widget/module_table_model.cpp b/plugins/gui/src/selection_details_widget/net_details_widget/module_table_model.cpp index 1dccb1bdf49..4bdebadb2bf 100644 --- a/plugins/gui/src/selection_details_widget/net_details_widget/module_table_model.cpp +++ b/plugins/gui/src/selection_details_widget/net_details_widget/module_table_model.cpp @@ -178,8 +178,10 @@ namespace hal return mEntries[index.row()].name; } - void ModuleTableModel::handleModulePortsChanged(Module* m) + void ModuleTableModel::handleModulePortsChanged(Module* m, PinEvent pev, u32 pgid) { + Q_UNUSED(pev); + Q_UNUSED(pgid); if (mModIds.find((int)m->get_id()) != mModIds.end()) { Net* net = gNetlist->get_net_by_id(mNetId); diff --git a/src/netlist/module.cpp b/src/netlist/module.cpp index 435f4856423..f65fd5d8c54 100644 --- a/src/netlist/module.cpp +++ b/src/netlist/module.cpp @@ -1357,6 +1357,8 @@ namespace hal bool Module::assign_pin_to_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups) { + u32 pin_group_id_to_delete = 0; + if (pin_group == nullptr) { log_warning("module", "could not assign pin to pin group of module '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); @@ -1397,6 +1399,7 @@ namespace hal if (delete_empty_groups && pg->empty()) { + pin_group_id_to_delete = pin_group->get_id(); if (!delete_pin_group_internal(pg)) { log_warning("module", @@ -1414,7 +1417,9 @@ namespace hal return false; } - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupPinAssign,pin_group->get_id())); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinAssignToGroup,pin->get_id())); + if (pin_group_id_to_delete) + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupDelete,pin_group_id_to_delete)); return true;; } @@ -1558,6 +1563,7 @@ namespace hal bool Module::remove_pin_net(Net* net) { + u32 pin_group_id_to_delete = 0; auto pin = get_pin_by_net(net); if (pin == nullptr) { @@ -1576,6 +1582,7 @@ namespace hal if (pin_group->empty()) { + pin_group_id_to_delete = pin_group->get_id(); if (!delete_pin_group_internal(pin_group)) { log_warning("module", "could not remove pin '{}' with ID {} from net '{}' with ID {}: failed to delete pin group '{}' with ID {}", pin->get_name(), pin->get_id(), net->get_name(), net->get_id(), pin_group->get_name(), pin_group->get_id()); @@ -1587,11 +1594,14 @@ namespace hal if (!delete_pin_internal(pin)) { + log_warning("module", "could not remove pin '{}' with ID {} from net '{}' with ID {}: failed to delete pin '{}' with ID {}", pin->get_name(), pin->get_id(), net->get_name(), net->get_id(), pin->get_name(), pin->get_id()); return false; } m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinDelete,pin_id_to_delete)); + if (pin_group_id_to_delete) + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupDelete,pin_group_id_to_delete)); return true; } From 9abb832ce08be4bd798966600c42e86041e96335 Mon Sep 17 00:00:00 2001 From: joern274 Date: Fri, 22 Sep 2023 11:24:10 +0200 Subject: [PATCH 14/66] Errors in pin_changed event handling fixed --- .../netlist/gate_library/enums/pin_type.h | 3 + .../module_details_widget/port_tree_model.h | 2 - .../module_details_widget/port_tree_model.cpp | 102 ++++++++---------- src/netlist/gate_library/enums/pin_type.cpp | 20 +++- src/netlist/module.cpp | 18 ++-- 5 files changed, 74 insertions(+), 71 deletions(-) diff --git a/include/hal_core/netlist/gate_library/enums/pin_type.h b/include/hal_core/netlist/gate_library/enums/pin_type.h index 6f8b660ad07..9f595c660ad 100644 --- a/include/hal_core/netlist/gate_library/enums/pin_type.h +++ b/include/hal_core/netlist/gate_library/enums/pin_type.h @@ -72,4 +72,7 @@ namespace hal PinDirChange, PinDelete }; + + template<> + std::map EnumStrings::data; } // namespace hal diff --git a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h index 938f683caa0..52604b5dd51 100644 --- a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h @@ -50,8 +50,6 @@ namespace hal PinType mPinType; QString mNetName; - void setPinType(const QString& type); - void setPinDirection(const QString& dir); public: PortTreeItem(Type itype, QString pinName, PinDirection dir, PinType ptype, QString netName = QString()); diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index edaa73f4917..4263aea1c22 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -39,32 +39,12 @@ namespace hal return QVariant(); } - void PortTreeItem::setPinType(const QString& type) - { - mPinType = PinType::none; - for (int pt = 1; pt < (int) PinType::sum; ++pt) - { - if (QString::fromStdString(enum_to_string((PinType)pt)) == type) - mPinType = (PinType)pt; - } - } - - void PortTreeItem::setPinDirection(const QString& dir) - { - mPinDirection = PinDirection::none; - for (int pd = 1; pd <= (int) PinDirection::internal; ++pd) - { - if (QString::fromStdString(enum_to_string((PinDirection)pd)) == dir) - mPinDirection = (PinDirection)pd; - } - } - void PortTreeItem::setData(QList data) { - mPinName = data[0].toString(); - setPinDirection(data[1].toString()); - setPinType(data[2].toString()); - mNetName = data[3].toString(); + mPinName = data[0].toString(); + mPinDirection = enum_from_string(data[1].toString().toStdString()); + mPinType = enum_from_string(data[2].toString().toStdString()); + mNetName = data[3].toString(); } void PortTreeItem::setDataAtIndex(int index, QVariant &data) @@ -75,10 +55,10 @@ namespace hal mPinName = data.toString(); break;} case 1: { - setPinDirection(data.toString()); + mPinDirection = enum_from_string(data.toString().toStdString()); break;} case 2: { - setPinType(data.toString()); + mPinType = enum_from_string(data.toString().toStdString()); break;} case 3: { mNetName = data.toString(); @@ -361,7 +341,7 @@ namespace hal QDataStream dataStream(&encItem, QIODevice::ReadOnly); dataStream >> type >> id; auto parentItem = static_cast(getItemFromIndex(parent)); - qDebug() << "type: " << type << ", id" << id << ", row: " << row; + // qDebug() << "type: " << type << ", id" << id << ", row: " << row; // construct a "drop-matrix" here, but only 4(5) things are NOT allowed (so check for these): // 1: drop a pin on its OWN parent @@ -530,40 +510,50 @@ namespace hal Q_UNUSED(pgid); if ((int)m->get_id() != mModuleId) return; + log_info("gui", "Handle pin_changed event {} ID={}", enum_to_string(pev), pgid); PortTreeItem* ptiPin = nullptr; PortTreeItem* ptiGroup = nullptr; const PinGroup* pgroup = nullptr; const ModulePin* pin = nullptr; int pinRow = -1; + if (pev < PinEvent::PinCreate) { // group event ptiGroup = mIdToGroupItem.value(pgid); - pgroup = m->get_pin_group_by_id(pgid); - if (!pgroup) + if (pev != PinEvent::GroupDelete) { - log_warning("gui", "Cannot handle event for pin group ID={}, no such group.", pgid); - return; + pgroup = m->get_pin_group_by_id(pgid); + if (!pgroup) + { + log_warning("gui", "Cannot handle event for pin group ID={}, no such group.", pgid); + return; + } } } else { // pin event ptiPin = mIdToPinItem.value(pgid); - pin = m->get_pin_by_id(pgid); - if (!pin) + if (pev != PinEvent::PinDelete) { - log_warning("gui", "Cannot handle event for pin ID={}, no such pid.", pgid); - return; + pin = m->get_pin_by_id(pgid); + if (!pin) + { + log_warning("gui", "Cannot handle event for pin ID={}, no such pid.", pgid); + return; + } + auto pgPair = pin->get_group(); + pgroup = pgPair.first; + pinRow = pgPair.second; + if (pgroup) + ptiGroup = mIdToGroupItem.value(pgroup->get_id()); } - auto pgPair = pin->get_group(); - pgroup = pgPair.first; - pinRow = pgPair.second; - if (pgroup) - ptiGroup = mIdToGroupItem.value(pgroup->get_id()); } + QModelIndex dataChangedIndex; + switch (pev) { case PinEvent::GroupCreate: @@ -585,12 +575,15 @@ namespace hal } case PinEvent::GroupRename: ptiGroup->setName(QString::fromStdString(pgroup->get_name())); + dataChangedIndex = getIndexFromItem(ptiGroup); break; case PinEvent::GroupTypeChange: ptiGroup->setPinType(pgroup->get_type()); + dataChangedIndex = getIndexFromItem(ptiGroup); break; case PinEvent::GroupDirChange: ptiGroup->setPinDirection(pgroup->get_direction()); + dataChangedIndex = getIndexFromItem(ptiGroup); break; case PinEvent::GroupDelete: removeItem(ptiGroup); @@ -639,12 +632,15 @@ namespace hal } case PinEvent::PinRename: ptiPin->setName(QString::fromStdString(pin->get_name())); + dataChangedIndex = getIndexFromItem(ptiPin); break; case PinEvent::PinTypeChange: ptiPin->setPinType(pin->get_type()); + dataChangedIndex = getIndexFromItem(ptiPin); break; case PinEvent::PinDirChange: ptiPin->setPinDirection(pin->get_direction()); + dataChangedIndex = getIndexFromItem(ptiPin); break; case PinEvent::PinDelete: removeItem(ptiPin); @@ -653,6 +649,12 @@ namespace hal default: break; } + + if (dataChangedIndex.isValid()) + { + QModelIndex inxLastCol = createIndex(dataChangedIndex.row(),columnCount()-1,dataChangedIndex.internalPointer()); + Q_EMIT dataChanged(dataChangedIndex,inxLastCol); + } } void ModulePinsTreeModel::dndGroupOnGroup(BaseTreeItem *droppedGroup, BaseTreeItem *onDroppedGroup) @@ -684,24 +686,13 @@ namespace hal if(ownRow < row && !bottomEdge) desiredIdx--; ActionPingroup* act = new ActionPingroup(PinActionType::GroupMove,droppedGroup->id(),"",desiredIdx); act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); - bool ok = act->exec(); - if(ok){ - removeItem(droppedGroup); - insertItem(droppedGroup, mRootItem, desiredIdx); - } + act->exec(); } void ModulePinsTreeModel::dndPinOnGroup(PortTreeItem *droppedPin, BaseTreeItem *onDroppedGroup) { ActionPingroup* act = new ActionPingroup(PinActionType::PinAsignGroup,droppedPin->id(),"",static_cast(onDroppedGroup)->id()); act->exec(); - auto oldParent = droppedPin->getParent(); - removeItem(droppedPin); - insertItem(droppedPin, onDroppedGroup, onDroppedGroup->getChildCount()); - if(!(oldParent->getChildCount())){ - removeItem(static_cast(oldParent)); - delete oldParent; - } } void ModulePinsTreeModel::dndPinBetweenPin(PortTreeItem *droppedPin, BaseTreeItem *onDroppedParent, int row) @@ -723,13 +714,6 @@ namespace hal } act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); act->exec(); - auto oldParent = droppedPin->getParent(); - removeItem(droppedPin); - insertItem(droppedPin, onDroppedParent, desiredIdx); - if(!(oldParent->getChildCount())){ - removeItem(static_cast(oldParent)); - delete oldParent; - } } void ModulePinsTreeModel::dndPinBetweenGroup(PortTreeItem *droppedPin, int row) diff --git a/src/netlist/gate_library/enums/pin_type.cpp b/src/netlist/gate_library/enums/pin_type.cpp index e39bee1b366..b9e9c01a296 100644 --- a/src/netlist/gate_library/enums/pin_type.cpp +++ b/src/netlist/gate_library/enums/pin_type.cpp @@ -19,4 +19,22 @@ namespace hal {PinType::select, "select"}, {PinType::carry, "carry"}, {PinType::sum, "sum"}}; -} \ No newline at end of file + + template<> + std::map EnumStrings::data = { + {PinEvent::unknown, "unknown"}, + {PinEvent::GroupCreate, "GroupCreate"}, + {PinEvent::GroupReorder, "GroupReorder"}, + {PinEvent::GroupRename, "GroupRename"}, + {PinEvent::GroupTypeChange, "GroupTypeChange"}, + {PinEvent::GroupDirChange, "GroupDirChange"}, + {PinEvent::GroupDelete, "GroupDelete"}, + {PinEvent::PinCreate, "PinCreate"}, + {PinEvent::PinReorder, "PinReorder"}, + {PinEvent::PinAssignToGroup, "PinAssignToGroup"}, + {PinEvent::PinRename, "PinRename"}, + {PinEvent::PinTypeChange, "PinTypeChange"}, + {PinEvent::PinDirChange, "PinDirChange"}, + {PinEvent::PinDelete, "PinDelete"} + }; +} diff --git a/src/netlist/module.cpp b/src/netlist/module.cpp index f65fd5d8c54..5c9fc41aedd 100644 --- a/src/netlist/module.cpp +++ b/src/netlist/module.cpp @@ -1272,6 +1272,7 @@ namespace hal bool Module::delete_pin_group(PinGroup* pin_group) { + std::vector distribute_events; if (pin_group == nullptr) { log_warning("module", "could not delete pin group from module '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); @@ -1286,16 +1287,16 @@ namespace hal return false; } - bool removed_pins = false; - std::vector pins_copy = pin_group->get_pins(); for (ModulePin* pin : pins_copy) { - removed_pins = true; - if (auto res = create_pin_group(pin->get_name(), {pin}, pin->get_direction(), pin->get_type(), true, 0, false); res.is_error()) + auto res = create_pin_group(pin->get_name(), {pin}, pin->get_direction(), pin->get_type(), true, 0, false); + if (res.is_error()) { return false; } + distribute_events.push_back(pinevent_associated_data(PinEvent::GroupCreate,res.get()->get_id())); + distribute_events.push_back(pinevent_associated_data(PinEvent::PinAssignToGroup,pin->get_id())); } u32 pin_group_id_to_delete = pin_group->get_id(); @@ -1305,10 +1306,9 @@ namespace hal return false; } - if (removed_pins) - { - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupDelete,pin_group_id_to_delete)); - } + for (u32 dist_ev : distribute_events) + m_event_handler->notify(ModuleEvent::event::pin_changed, this, dist_ev); + m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupDelete,pin_group_id_to_delete)); return true; } @@ -1399,7 +1399,7 @@ namespace hal if (delete_empty_groups && pg->empty()) { - pin_group_id_to_delete = pin_group->get_id(); + pin_group_id_to_delete = pg->get_id(); if (!delete_pin_group_internal(pg)) { log_warning("module", From 7c49225ddbc4e893b783d39e32244777c598ce78 Mon Sep 17 00:00:00 2001 From: joern274 Date: Tue, 26 Sep 2023 22:28:54 +0200 Subject: [PATCH 15/66] Modified assign behaviour on descending pin groups --- include/hal_core/netlist/pins/base_pin.h | 13 +++++++++- include/hal_core/netlist/pins/pin_group.h | 26 ++++++++++++++----- .../include/gui/user_action/action_pingroup.h | 1 + .../gui/src/user_action/action_pingroup.cpp | 24 +++++++++++++++++ .../netlist_modification_decorator.cpp | 2 +- src/netlist/gate_library/gate_type.cpp | 5 ++-- src/netlist/module.cpp | 11 ++++---- 7 files changed, 64 insertions(+), 18 deletions(-) diff --git a/include/hal_core/netlist/pins/base_pin.h b/include/hal_core/netlist/pins/base_pin.h index 79629f2c34c..b168aeb1655 100644 --- a/include/hal_core/netlist/pins/base_pin.h +++ b/include/hal_core/netlist/pins/base_pin.h @@ -100,6 +100,17 @@ namespace hal m_name = name; } + int get_pos() const + { + auto g = get_group(); + int i = 0; + for (auto p : g.first->get_pins()) + { + if (p == this) return i; + ++i; + } + return -1; + } /** * Get the name of the pin. * @@ -187,4 +198,4 @@ namespace hal { } }; -} // namespace hal \ No newline at end of file +} // namespace hal diff --git a/include/hal_core/netlist/pins/pin_group.h b/include/hal_core/netlist/pins/pin_group.h index b036ab682d1..649467672f3 100644 --- a/include/hal_core/netlist/pins/pin_group.h +++ b/include/hal_core/netlist/pins/pin_group.h @@ -27,6 +27,7 @@ #include "hal_core/defines.h" #include "hal_core/utilities/result.h" +#include "hal_core/utilities/log.h" #include #include @@ -312,19 +313,30 @@ namespace hal * Assign a pin to the pin group. * * @param[in] pin - The pin to assign. - * @returns Ok on success, an error message otherwise. + * @returns true on success, false otherwise. */ - Result assign_pin(T* pin) + bool assign_pin(T* pin) { if (pin == nullptr) { - return ERR("'nullptr' given instead of a pin when trying to assign a pin to pin group '" + m_name + "' with ID " + std::to_string(m_id)); + log_warning("pin_group", "'nullptr' given instead of a pin when trying to assign a pin to pin group '{}' with ID {}", + m_name, m_id); + return false; } - i32 index = m_ascending ? m_next_index++ : m_next_index--; - m_pins.push_back(pin); + i32 index = 0; + if (m_ascending) + { + index = m_next_index++; + m_pins.push_back(pin); + } + else + { + index = ++m_start_index; + m_pins.push_front(pin); + } pin->m_group = std::make_pair(this, index); - return OK({}); + return true; } /** @@ -481,4 +493,4 @@ namespace hal PinGroup& operator=(const PinGroup&) = delete; PinGroup& operator=(PinGroup&&) = delete; }; -} // namespace hal \ No newline at end of file +} // namespace hal diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index 9c9c075129d..37fedce3b16 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -48,6 +48,7 @@ namespace hal int pinGroupIndex(const Module* mod, const PinGroup* pgrp); + void dumpPingroups(Module* m = nullptr); /** * @ingroup user_action * @brief Pingroup user actions diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index a59affc1332..80526654db7 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -19,6 +19,22 @@ namespace hal return -1; } + void dumpPingroups(Module *m) + { + if (!m) m = gNetlist->get_top_module(); + std::cerr << "module: " << m->get_id() << " <" << m->get_name() << ">\n"; + for (PinGroup* pg : m->get_pin_groups()) + { + std::cerr << " grp: " << pg->get_id() << (pg->is_ascending()?" asc ": " des ") << pg->get_start_index() + << " <" << pg->get_name() << ">\n"; + for (ModulePin* pin : pg->get_pins()) + std::cerr << " pin: " << pin->get_id() << " inx:" << pin->get_group().second << " pos:" << pin->get_pos() << " <" << pin->get_name() << ">\n"; + } + std::cerr << "-------------" << std::endl; + for (Module* sm : m->get_submodules()) + dumpPingroups(sm); + } + QString PinActionType::toString(PinActionType::Type tp) { QMetaEnum me = QMetaEnum::fromType(); @@ -342,7 +358,11 @@ namespace hal pgroup = getGroup(aa.mValue); if (!pgroup) return false; if (!mParentModule->assign_pin_to_group(pgroup,pin)) + { + qDebug() << "assign_pin_to_group failed"; return false; + } + dumpPingroups(); break; case PinActionType::PinRename: addUndoAction(PinActionType::PinRename,aa.mId, QString::fromStdString(pin->get_name())); @@ -359,7 +379,11 @@ namespace hal addUndoAction(PinActionType::PinSetindex,aa.mId,"",pin->get_group().second); pgroup = pin->get_group().first; if (!mParentModule->move_pin_within_group(pgroup,pin,aa.mValue)) + { + qDebug() << "move_pin_within_group failed"; return false; + } + dumpPingroups(); break; default: break; diff --git a/src/netlist/decorators/netlist_modification_decorator.cpp b/src/netlist/decorators/netlist_modification_decorator.cpp index 1b37bd13f17..73bd446de32 100644 --- a/src/netlist/decorators/netlist_modification_decorator.cpp +++ b/src/netlist/decorators/netlist_modification_decorator.cpp @@ -326,7 +326,7 @@ namespace hal pin_group = pin_groups.front(); } - pin_group->assign_pin(pin).get(); + pin_group->assign_pin(pin); if (const auto res = pin_group->move_pin(pin, pin_index); res.is_error()) { return ERR_APPEND(res.get_error(), diff --git a/src/netlist/gate_library/gate_type.cpp b/src/netlist/gate_library/gate_type.cpp index c8e2d2672ff..3a4050ed2f4 100644 --- a/src/netlist/gate_library/gate_type.cpp +++ b/src/netlist/gate_library/gate_type.cpp @@ -507,10 +507,9 @@ namespace hal } } - if (auto res = pin_group->assign_pin(pin); res.is_error()) + if (!pin_group->assign_pin(pin)) { - return ERR_APPEND(res.get_error(), - "could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " + return ERR("could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " of gate type '" + m_name + "' with ID " + std::to_string(m_id)); } diff --git a/src/netlist/module.cpp b/src/netlist/module.cpp index 5c9fc41aedd..af1e0a16e23 100644 --- a/src/netlist/module.cpp +++ b/src/netlist/module.cpp @@ -842,12 +842,11 @@ namespace hal else { // create_pin_group_internal OK - if (const auto assign_res = group_res.get()->assign_pin(pin_res.get()); assign_res.is_error()) + if (!group_res.get()->assign_pin(pin_res.get())) { assert(delete_pin_internal(pin_res.get())); assert(delete_pin_group_internal(group_res.get())); - return ERR_APPEND(assign_res.get_error(), - "could not create pin '" + name + "' for module '" + m_name + "' with ID " + std::to_string(m_id) + ": failed to assign pin to pin group"); + return ERR("could not create pin '" + name + "' for module '" + m_name + "' with ID " + std::to_string(m_id) + ": failed to assign pin to pin group"); } else { @@ -1410,7 +1409,7 @@ namespace hal } } - if (auto res = pin_group->assign_pin(pin); res.is_error()) + if (!pin_group->assign_pin(pin)) { log_warning("module", "could not assign pin '{}' with ID {} to pin group '{}' with ID {} of module '{}' with ID {}", pin->get_name(), pin->get_id(), pin_group->get_name(), pin_group->get_id(), m_name, m_id); @@ -1551,9 +1550,9 @@ namespace hal } else { - if (const auto assign_res = group_res.get()->assign_pin(pin); assign_res.is_error()) + if (!group_res.get()->assign_pin(pin)) { - return ERR_APPEND(assign_res.get_error(), "could not assign pin '" + name_internal + "' to net: failed to assign pin to pin group"); + return ERR("could not assign pin '" + name_internal + "' to net: failed to assign pin to pin group"); } } From 05890be0c1ff5f0a2a835e8e4e396dab2b05beb5 Mon Sep 17 00:00:00 2001 From: joern274 Date: Thu, 28 Sep 2023 21:09:56 +0200 Subject: [PATCH 16/66] All pin_changed functions except dndPinBetweenGroup implemented and tested --- include/hal_core/netlist/pins/pin_group.h | 11 +++++++- .../include/gui/user_action/action_pingroup.h | 4 +++ .../module_details_widget/port_tree_model.cpp | 4 ++- .../gui/src/user_action/action_pingroup.cpp | 25 ++++++++++++++++--- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/include/hal_core/netlist/pins/pin_group.h b/include/hal_core/netlist/pins/pin_group.h index 649467672f3..2e125ff46b9 100644 --- a/include/hal_core/netlist/pins/pin_group.h +++ b/include/hal_core/netlist/pins/pin_group.h @@ -332,7 +332,16 @@ namespace hal } else { - index = ++m_start_index; + if (m_start_index == m_next_index) + { + // special case empty pin group + index = m_start_index; + -- m_next_index; + } + else + { + index = ++m_start_index; + } m_pins.push_front(pin); } pin->m_group = std::make_pair(this, index); diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index 37fedce3b16..d1562ff4ed7 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -48,6 +48,10 @@ namespace hal int pinGroupIndex(const Module* mod, const PinGroup* pgrp); + int pinIndex2Row(const ModulePin* pin, int index); + + int pinRow2Index(const ModulePin* pin, int row); + void dumpPingroups(Module* m = nullptr); /** * @ingroup user_action diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index 4263aea1c22..b0ceed5effe 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -546,7 +546,7 @@ namespace hal } auto pgPair = pin->get_group(); pgroup = pgPair.first; - pinRow = pgPair.second; + pinRow = pinIndex2Row(pin,pgPair.second); if (pgroup) ptiGroup = mIdToGroupItem.value(pgroup->get_id()); } @@ -672,6 +672,7 @@ namespace hal auto tgtgroup = mModule->get_pin_group_by_id(static_cast(onDroppedGroup)->id()); ActionPingroup* act = ActionPingroup::addPinsToExistingGroup(mModule,tgtgroup->get_id(),pins); + act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); if (act) act->exec(); // too keep the order, ActionAddItemsToObject cannot be executed with all pins, but a ComAction must be created @@ -692,6 +693,7 @@ namespace hal void ModulePinsTreeModel::dndPinOnGroup(PortTreeItem *droppedPin, BaseTreeItem *onDroppedGroup) { ActionPingroup* act = new ActionPingroup(PinActionType::PinAsignGroup,droppedPin->id(),"",static_cast(onDroppedGroup)->id()); + act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); act->exec(); } diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index 80526654db7..3eb268f00c9 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -28,13 +28,30 @@ namespace hal std::cerr << " grp: " << pg->get_id() << (pg->is_ascending()?" asc ": " des ") << pg->get_start_index() << " <" << pg->get_name() << ">\n"; for (ModulePin* pin : pg->get_pins()) - std::cerr << " pin: " << pin->get_id() << " inx:" << pin->get_group().second << " pos:" << pin->get_pos() << " <" << pin->get_name() << ">\n"; + std::cerr << " pin: " << pin->get_id() << " inx:" << pin->get_group().second << " pos:" << pin->get_pos() << " row:" + << pinIndex2Row(pin,pin->get_group().second) << " <" << pin->get_name() << ">\n"; } std::cerr << "-------------" << std::endl; for (Module* sm : m->get_submodules()) dumpPingroups(sm); } + int pinIndex2Row(const ModulePin* pin, int index) + { + auto pg = pin->get_group(); + if (pg.first->is_ascending()) + return index - pg.first->get_start_index(); + return pg.first->get_start_index() - index; + } + + int pinRow2Index(const ModulePin* pin, int row) + { + auto pg = pin->get_group(); + if (pg.first->is_ascending()) + return pg.first->get_start_index() + row; + return pg.first->get_start_index() - row; + } + QString PinActionType::toString(PinActionType::Type tp) { QMetaEnum me = QMetaEnum::fromType(); @@ -353,7 +370,7 @@ namespace hal break; case PinActionType::PinAsignGroup: addUndoAction(PinActionType::PinAsignGroup,aa.mId,"",pin->get_group().first->get_id()); - addUndoAction(PinActionType::PinSetindex,aa.mId,"",pin->get_group().second); + addUndoAction(PinActionType::PinSetindex,aa.mId,"",pinIndex2Row(pin,pin->get_group().second)); mPinsMoved.insert(aa.mId); pgroup = getGroup(aa.mValue); if (!pgroup) return false; @@ -376,9 +393,9 @@ namespace hal break; case PinActionType::PinSetindex: if (!mPinsMoved.contains(aa.mId)) - addUndoAction(PinActionType::PinSetindex,aa.mId,"",pin->get_group().second); + addUndoAction(PinActionType::PinSetindex,aa.mId,"",pinIndex2Row(pin,pin->get_group().second)); pgroup = pin->get_group().first; - if (!mParentModule->move_pin_within_group(pgroup,pin,aa.mValue)) + if (!mParentModule->move_pin_within_group(pgroup,pin,pinRow2Index(pin,aa.mValue))) { qDebug() << "move_pin_within_group failed"; return false; From bcaca5d374aefbbd4c68534a8a9fe681ca7e66ca Mon Sep 17 00:00:00 2001 From: joern274 Date: Fri, 29 Sep 2023 17:10:36 +0200 Subject: [PATCH 17/66] Bugfixes and implementation of dndPinBetweenGroup --- .../module_details_widget/port_tree_model.h | 3 +- .../include/gui/user_action/action_pingroup.h | 10 ++-- .../module_details_widget/port_tree_model.cpp | 51 +++++-------------- .../gui/src/user_action/action_pingroup.cpp | 32 +++++++++--- 4 files changed, 44 insertions(+), 52 deletions(-) diff --git a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h index 52604b5dd51..575b4637d93 100644 --- a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h @@ -52,7 +52,7 @@ namespace hal public: - PortTreeItem(Type itype, QString pinName, PinDirection dir, PinType ptype, QString netName = QString()); + PortTreeItem(Type itype, u32 id_, QString pinName, PinDirection dir, PinType ptype, QString netName = QString()); PortTreeItem() : mItemType(None), mId(0) {;} QVariant getData(int column) const override; void setData(QList data) override; @@ -61,7 +61,6 @@ namespace hal int getColumnCount() const override; void setItemType(Type tp) { mItemType = tp; } Type itemType() const { return mItemType; } - void setId(u32 id_) { mId = id_; } QString name() const { return mPinName; } void setName(const QString& nam) { mPinName = nam; } void setPinType(PinType ptype) { mPinType = ptype; } diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index d1562ff4ed7..f838cd72a21 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -52,6 +52,8 @@ namespace hal int pinRow2Index(const ModulePin* pin, int row); + QString generateGroupName(const Module* mod, const ModulePin* pin); + void dumpPingroups(Module* m = nullptr); /** * @ingroup user_action @@ -154,10 +156,10 @@ namespace hal void readFromXml(QXmlStreamReader& xmlIn) override; void addToHash(QCryptographicHash& cryptoHash) const override; - static ActionPingroup* addPinsToExistingGroup(const Module* m, u32 grpId, QList pinIds, int irow = -1); - static ActionPingroup* addPinToExistingGroup(const Module* m, u32 grpId, u32 pinId, int irow = -1); - static ActionPingroup* addPinsToNewGroup(const Module* m, const QString& name, QList pinIds); - static ActionPingroup* addPinToNewGroup(const Module* m, const QString& name, u32 pinId); + static ActionPingroup* addPinsToExistingGroup(const Module* m, u32 grpId, QList pinIds, int pinRow = -1); + static ActionPingroup* addPinToExistingGroup(const Module* m, u32 grpId, u32 pinId, int pinRow = -1); + static ActionPingroup* addPinsToNewGroup(const Module* m, const QString& name, QList pinIds, int grpRow = -1); + static ActionPingroup* addPinToNewGroup(const Module* m, const QString& name, u32 pinId, int grpRow = -1); static ActionPingroup* removePinsFromGroup(const Module* m, QList pinIds); }; diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index b0ceed5effe..a4e40653a3f 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -19,8 +19,8 @@ namespace hal { - PortTreeItem::PortTreeItem(Type itype, QString pinName, PinDirection dir, PinType ptype, QString netName) - : mItemType(itype), mPinName(pinName), mPinDirection(dir), mPinType(ptype), mNetName(netName) + PortTreeItem::PortTreeItem(Type itype, u32 id_, QString pinName, PinDirection dir, PinType ptype, QString netName) + : mItemType(itype), mId(id_), mPinName(pinName), mPinDirection(dir), mPinType(ptype), mNetName(netName) {;} QVariant PortTreeItem::getData(int index) const @@ -145,6 +145,7 @@ namespace hal data->setData("pintreemodel/item", encodedData); return data; } + bool ModulePinsTreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) { Q_UNUSED(action) @@ -154,6 +155,7 @@ namespace hal int id; QByteArray encItem = data->data("pintreemodel/item"); QDataStream dataStream(&encItem, QIODevice::ReadOnly); + qDebug() << "dropMimeData" << encItem << row << column; dataStream >> type >> id; auto droppedItem = (type == "group") ? static_cast(mIdToGroupItem.value(id)) : static_cast(mIdToPinItem.value(id)); @@ -405,17 +407,16 @@ namespace hal continue; auto pinGroupName = QString::fromStdString(pinGroup->get_name()); - PortTreeItem* pinGroupItem = new PortTreeItem(PortTreeItem::Group,pinGroupName, pinGroup->get_direction(), pinGroup->get_type()); - pinGroupItem->setId(pinGroup->get_id()); + PortTreeItem* pinGroupItem = new PortTreeItem(PortTreeItem::Group, pinGroup->get_id(), pinGroupName, pinGroup->get_direction(), pinGroup->get_type()); mIdToGroupItem.insert(pinGroup->get_id(), pinGroupItem); for(ModulePin* pin : pinGroup->get_pins()) { PortTreeItem* pinItem = new PortTreeItem(PortTreeItem::Pin, + pin->get_id(), QString::fromStdString(pin->get_name()), pin->get_direction(), pin->get_type(), QString::fromStdString(pin->get_net()->get_name())); - pinItem->setId(pin->get_id()); pinGroupItem->appendChild(pinItem); mNameToTreeItem.insert(QString::fromStdString(pin->get_name()), pinItem); mIdToPinItem.insert(pin->get_id(), pinItem); @@ -559,9 +560,11 @@ namespace hal case PinEvent::GroupCreate: { ptiGroup = new PortTreeItem(PortTreeItem::Group, + pgroup->get_id(), QString::fromStdString(pgroup->get_name()), pgroup->get_direction(), pgroup->get_type()); + mIdToGroupItem.insert(ptiGroup->id(), ptiGroup); int inx = pinGroupIndex(m,pgroup); insertItem(ptiGroup, mRootItem, inx); break; @@ -600,11 +603,12 @@ namespace hal if (pin->get_net()) netName = QString::fromStdString(pin->get_net()->get_name()); ptiPin = new PortTreeItem(PortTreeItem::Pin, + pin->get_id(), QString::fromStdString(pin->get_name()), pin->get_direction(), pin->get_type(), netName); - + mIdToPinItem.insert(ptiPin->id(), ptiPin); insertItem(ptiPin, ptiGroup, pinRow); break; } @@ -726,39 +730,10 @@ namespace hal auto pinToMove = mModule->get_pin_by_id(droppedPin->id()); if (!pinToMove) return; - QString groupName = QString::fromStdString(pinToMove->get_name()); - QString baseName = groupName; - int cnt = 2; - while (mModule->get_pin_group_by_name(groupName.toStdString())) - // pin group name already exists - groupName = QString("%1_%2").arg(baseName).arg(cnt++); -/* - ActionPingroup* actMovePin = new ActionPingroup(pinToMove->get_id(),0,0,groupName); - actMovePin->setObject(UserActionObject(mModuleId, UserActionObjectType::Module)); - bool ok = actMovePin->exec(); - if (ok && actMovePin->targetGroupId()) - { - ActionPingroup* actMoveGroup = new ActionPingroup(PinAction::MoveGroup,actMovePin->targetGroupId()); - actMoveGroup->setObject(UserActionObject(mModuleId, UserActionObjectType::Module)); - actMoveGroup->setPinOrderNo(row); - actMoveGroup->exec(); - - auto newGroup = mModule->get_pin_by_id(getIdOfItem(droppedPin))->get_group().first; - auto pinGroupName = QString::fromStdString(newGroup->get_name()); - auto pinGroupDirection = QString::fromStdString(enum_to_string((newGroup->get_direction()))); - auto pinGroupType = QString::fromStdString(enum_to_string(newGroup->get_type())); + QString groupName = generateGroupName(mModule,pinToMove); - PortTreeItem* pinGroupItem = new PortTreeItem(pinGroupName, pinGroupDirection, pinGroupType, ""); - pinGroupItem->setAdditionalData(keyType, QVariant::fromValue(itemType::group)); - pinGroupItem->setAdditionalData(keyId, newGroup->get_id()); - - int pos = mRootItem->getChildCount(); // or query current index - - insertItem(pinGroupItem, mRootItem, pos); - removeItem(droppedPin); - insertItem(droppedPin, pinGroupItem, 0); - } - */ + ActionPingroup* act = ActionPingroup::addPinToNewGroup(mModule, groupName, droppedPin->id(),row); + act->exec(); } void ModulePinsTreeModel::insertItem(PortTreeItem* item, BaseTreeItem* parent, int index) diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index 3eb268f00c9..903102888b9 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -52,6 +52,19 @@ namespace hal return pg.first->get_start_index() - row; } + QString generateGroupName(const Module* mod, const ModulePin* pin) + { + QString baseName = QString::fromStdString(pin->get_name()); + QSet existingGroups; + for (auto g : mod->get_pin_groups()) + existingGroups.insert(QString::fromStdString(g->get_name())); + QString retval = baseName; + int count = 1; + while (existingGroups.contains(retval)) + retval = QString("%1_%2").arg(baseName).arg(++count); + return retval; + } + QString PinActionType::toString(PinActionType::Type tp) { QMetaEnum me = QMetaEnum::fromType(); @@ -412,7 +425,7 @@ namespace hal return UserAction::exec(); } - ActionPingroup* ActionPingroup::addPinsToExistingGroup(const Module *m, u32 grpId, QList pinIds, int irow) + ActionPingroup* ActionPingroup::addPinsToExistingGroup(const Module *m, u32 grpId, QList pinIds, int pinRow) { ActionPingroup* retval = nullptr; for (u32 pinId : pinIds) @@ -421,21 +434,21 @@ namespace hal retval->mPinActions.append(AtomicAction(PinActionType::PinAsignGroup,pinId,"",grpId)); else retval = new ActionPingroup(PinActionType::PinAsignGroup,pinId,"",grpId); - if (irow >= 0) - retval->mPinActions.append(AtomicAction(PinActionType::PinSetindex,pinId,"",irow++)); + if (pinRow >= 0) + retval->mPinActions.append(AtomicAction(PinActionType::PinSetindex,pinId,"",pinRow++)); } retval->setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); return retval; } - ActionPingroup* ActionPingroup::addPinToExistingGroup(const Module* m, u32 grpId, u32 pinId, int irow) + ActionPingroup* ActionPingroup::addPinToExistingGroup(const Module* m, u32 grpId, u32 pinId, int pinRow) { QList pinIds; pinIds << pinId; - return addPinsToExistingGroup(m,grpId,pinIds,irow); + return addPinsToExistingGroup(m,grpId,pinIds,pinRow); } - ActionPingroup* ActionPingroup::addPinsToNewGroup(const Module* m, const QString& name, QList pinIds) + ActionPingroup* ActionPingroup::addPinsToNewGroup(const Module* m, const QString& name, QList pinIds, int grpRow) { static int vid = -9; ActionPingroup* retval = new ActionPingroup(PinActionType::GroupCreate,vid,name); @@ -450,15 +463,18 @@ namespace hal } for (u32 pinId : pinIds) retval->mPinActions.append(AtomicAction(PinActionType::PinAsignGroup,pinId,"",vid)); + + if (grpRow >= 0) + retval->mPinActions.append(AtomicAction(PinActionType::GroupMove,vid,"",grpRow)); retval->setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); return retval; } - ActionPingroup* ActionPingroup::addPinToNewGroup(const Module *m, const QString& name, u32 pinId) + ActionPingroup* ActionPingroup::addPinToNewGroup(const Module *m, const QString& name, u32 pinId, int grpRow) { QList pinIds; pinIds << pinId; - return addPinsToNewGroup(m,name,pinIds); + return addPinsToNewGroup(m,name,pinIds, grpRow); } ActionPingroup* ActionPingroup::removePinsFromGroup(const Module* m, QList pinIds) From 6ca908a297da291f0af9effed2315b5daef2140c Mon Sep 17 00:00:00 2001 From: joern274 Date: Sat, 30 Sep 2023 22:16:33 +0200 Subject: [PATCH 18/66] Stacking of pin_changed events finally solved --- CHANGELOG.md | 5 + .../netlist/gate_library/enums/pin_event.h | 166 ++++++++++++++++++ .../netlist/gate_library/enums/pin_type.h | 20 --- include/hal_core/netlist/module.h | 9 +- .../gui/graph_widget/graph_context_manager.h | 2 +- .../include/gui/netlist_relay/netlist_relay.h | 2 +- .../module_details_widget/port_tree_model.h | 3 +- .../net_details_widget/module_table_model.h | 2 +- .../include/gui/user_action/action_pingroup.h | 2 + .../module_details_widget/port_tree_model.cpp | 8 +- .../gui/src/user_action/action_pingroup.cpp | 5 + src/netlist/gate_library/enums/pin_event.cpp | 102 +++++++++++ src/netlist/gate_library/enums/pin_type.cpp | 18 -- src/netlist/module.cpp | 67 ++++--- 14 files changed, 332 insertions(+), 79 deletions(-) create mode 100644 include/hal_core/netlist/gate_library/enums/pin_event.h create mode 100644 src/netlist/gate_library/enums/pin_event.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index dd0aefd1bb4..854d92cb029 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,11 @@ All notable changes to this project will be documented in this file. * fixed order of pins within pin groups not being properly handled for modules and gate types * fixed netlist parsers assigning gate pins in wrong order (compensated by the bug above, imported netlists were still correct) * fixed wrong order of pins within pin groups in provided gate libraries +* module pins + * added qualifier for `pin_changed` core events telling receiver details about the recent modification + * added event scope and stacking classes so that `pin_changed` events can be collected and prioritized + * added specific GUI handler for every `pin_changed` event thus replacing the reload-entire-pingroup-tree policy + * added class `ActionPingroup` so that UNDO function works for all pin / pin group actions issued from GUI ## [4.2.0](v4.2.0) - 2023-05-24 10:02:04-07:00 (urgency: medium) * GUI plugin manager diff --git a/include/hal_core/netlist/gate_library/enums/pin_event.h b/include/hal_core/netlist/gate_library/enums/pin_event.h new file mode 100644 index 00000000000..7f4b6daf3c8 --- /dev/null +++ b/include/hal_core/netlist/gate_library/enums/pin_event.h @@ -0,0 +1,166 @@ +// MIT License +// +// Copyright (c) 2019 Ruhr University Bochum, Chair for Embedded Security. All Rights reserved. +// Copyright (c) 2019 Marc Fyrbiak, Sebastian Wallat, Max Hoffmann ("ORIGINAL AUTHORS"). All rights reserved. +// Copyright (c) 2021 Max Planck Institute for Security and Privacy. All Rights reserved. +// Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "hal_core/utilities/enums.h" +#include "hal_core/defines.h" +#include +#include + +namespace hal +{ + /** + * Spezifies the pin_changed event type + * + * The order of events in enum class defines the order in which events are handled. + * + */ + enum class PinEvent + { + unknown, + GroupCreate, /// new pin group created + GroupRename, /// pin group renamed + GroupTypeChange, /// changed PinType attribute of group (like data) + GroupDirChange, /// changed PinDirection attribute of group (like input) + GroupReorder, /// moved group to a new position within containing module + PinCreate, /// new pin created + PinAssignToGroup, /// pin assigned to new group + PinRename, /// pin renamed + PinTypeChange, /// changed PinType attribute of pin (like data) + PinDirChange, /// changed PinDirection attribute of pin (like input) + PinReorder, /// moved pin to a new position within containing group + PinDelete, /// pin deleted + GroupDelete /// group deleted + }; + + template<> + std::map EnumStrings::data; + + class Module; + + /** + * Wrapper class for core pin_changed events. + * + * Events can be send immediately or stacked and send at according to their priority. + */ + class PinChangedEvent + { + friend bool pin_event_order(const PinChangedEvent& a, const PinChangedEvent& b); + friend class PinChangedEventScope; + + /** + * Subclass for event stack. + */ + class EventStack : public std::vector + { + public: + /** + * Scope count indicates the nesting depth of event-throwing subroutines. + * Only the top level (m_count=0) is allowed to send the events from stack. + */ + int m_count; + + /** + * Construct empty stack + */ + EventStack() : m_count(0) {;} + + /** + * Attempts to send events, typically at the end of a pin-changing subroutine. + * Events will only be send if m_count is zero. + */ + void send_events(Module* m); + }; + + static std::unordered_map s_event_stack; + + Module* m_module; + PinEvent m_event; + u32 m_id; + + public: + /** + * PinChangedEvent class for single event + * @param m - The module comprising pins and pin groups + * @param pev - The pin event enum + * @param id - pin or pin group ID + */ + PinChangedEvent(Module* m, PinEvent pev, u32 id); + + /** + * Returns the module for which pins or pin groups have been changed + * @return The module comprising pins and pin groups + */ + Module* get_module() const; + + /** + * Return bitwise binary encoded PinEvent and ID + * 4LSB = The pin event enum as 4 bit int + * 28HSB = The ID as 28 bit int + * @return The bitcode according to scheme above + */ + u32 associated_data(); + + /** + * Attempts to send event. + * If this routine or any calling routine wants to collect events the event gets written on stack instead. + */ + void send(); + }; + + /** + * By creating an instance of this class a new scope gets created thus collecting events. + */ + class PinChangedEventScope + { + Module* m_module; + public: + + /** + * Constructor for scope instance incrementing scope count + * @param m The module comprising pins and pin groups + */ + PinChangedEventScope(Module* m); + + /** + * Destructor for scope instance decrementing scope count + */ + ~PinChangedEventScope(); + + /** + * Attempts to send all stacked events. Will do nothing if not issued from top-level scope. + */ + void send_events(); + }; + + /** + * Function used by sort algorithm to organize events according to their priority. + * @param a - Pin changed event A + * @param b - Pin changed event B + * @return true if A should be handled before B, false otherwise. + */ + bool pin_event_order(const PinChangedEvent& a, const PinChangedEvent& b); +} diff --git a/include/hal_core/netlist/gate_library/enums/pin_type.h b/include/hal_core/netlist/gate_library/enums/pin_type.h index 9f595c660ad..b8a35ef1a03 100644 --- a/include/hal_core/netlist/gate_library/enums/pin_type.h +++ b/include/hal_core/netlist/gate_library/enums/pin_type.h @@ -55,24 +55,4 @@ namespace hal template<> std::map EnumStrings::data; - enum class PinEvent - { - unknown, - GroupCreate, - GroupReorder, - GroupRename, - GroupTypeChange, - GroupDirChange, - GroupDelete, - PinCreate, - PinReorder, - PinAssignToGroup, - PinRename, - PinTypeChange, - PinDirChange, - PinDelete - }; - - template<> - std::map EnumStrings::data; } // namespace hal diff --git a/include/hal_core/netlist/module.h b/include/hal_core/netlist/module.h index 874102df6cc..361571a27cb 100644 --- a/include/hal_core/netlist/module.h +++ b/include/hal_core/netlist/module.h @@ -30,6 +30,7 @@ #include "hal_core/netlist/event_system/event_handler.h" #include "hal_core/netlist/gate_library/enums/pin_direction.h" #include "hal_core/netlist/gate_library/enums/pin_type.h" +#include "hal_core/netlist/gate_library/enums/pin_event.h" #include "hal_core/netlist/gate_library/gate_library.h" #include "hal_core/netlist/pins/module_pin.h" #include "hal_core/netlist/pins/pin_group.h" @@ -672,6 +673,12 @@ namespace hal */ std::vector get_gates(const std::function& filter, bool recursive = false) const; + /** + * Get the event handler connected to module + * @return The event handler; + */ + EventHandler* get_event_handler() const; + private: friend class NetlistInternalManager; Module(NetlistInternalManager* internal_manager, EventHandler* event_handler, u32 id, Module* parent, const std::string& name); @@ -687,8 +694,6 @@ namespace hal bool has_external_destination; }; - static u32 pinevent_associated_data(PinEvent pev, u32 id); - std::string m_name; std::string m_type; diff --git a/plugins/gui/include/gui/graph_widget/graph_context_manager.h b/plugins/gui/include/gui/graph_widget/graph_context_manager.h index 01940b25655..892f8dcb5db 100644 --- a/plugins/gui/include/gui/graph_widget/graph_context_manager.h +++ b/plugins/gui/include/gui/graph_widget/graph_context_manager.h @@ -26,7 +26,7 @@ #pragma once #include "hal_core/defines.h" -#include "hal_core/netlist/gate_library/enums/pin_type.h" +#include "hal_core/netlist/gate_library/enums/pin_event.h" #include #include diff --git a/plugins/gui/include/gui/netlist_relay/netlist_relay.h b/plugins/gui/include/gui/netlist_relay/netlist_relay.h index 6733a89cc50..88c49d1ae84 100644 --- a/plugins/gui/include/gui/netlist_relay/netlist_relay.h +++ b/plugins/gui/include/gui/netlist_relay/netlist_relay.h @@ -26,7 +26,7 @@ #pragma once #include "hal_core/netlist/event_system/event_handler.h" -#include "hal_core/netlist/gate_library/enums/pin_type.h" +#include "hal_core/netlist/gate_library/enums/pin_event.h" #include "gui/grouping/grouping_color_serializer.h" #include #include diff --git a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h index 575b4637d93..d14937a00d7 100644 --- a/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h +++ b/plugins/gui/include/gui/selection_details_widget/module_details_widget/port_tree_model.h @@ -27,8 +27,9 @@ //#include "gui/new_selection_details_widget/models/base_tree_model.h" #include "gui/basic_tree_model/base_tree_model.h" -#include "hal_core/netlist/gate_library/enums/pin_type.h" #include "hal_core/netlist/gate_library/enums/pin_direction.h" +#include "hal_core/netlist/gate_library/enums/pin_event.h" +#include "hal_core/netlist/gate_library/enums/pin_type.h" #include namespace hal diff --git a/plugins/gui/include/gui/selection_details_widget/net_details_widget/module_table_model.h b/plugins/gui/include/gui/selection_details_widget/net_details_widget/module_table_model.h index 7749794da7e..cfe0a053e7d 100644 --- a/plugins/gui/include/gui/selection_details_widget/net_details_widget/module_table_model.h +++ b/plugins/gui/include/gui/selection_details_widget/net_details_widget/module_table_model.h @@ -26,7 +26,7 @@ #pragma once #include "hal_core/defines.h" -#include "hal_core/netlist/gate_library/enums/pin_type.h" +#include "hal_core/netlist/gate_library/enums/pin_event.h" #include #include diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index f838cd72a21..375cc0ff2a1 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -174,4 +174,6 @@ namespace hal UserAction* newAction() const; static ActionPingroupFactory* sFactory; }; + + uint qHash(PinEvent pev); } diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index a4e40653a3f..b8ab9496658 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -507,6 +507,12 @@ namespace hal void ModulePinsTreeModel::handleModulePortsChanged(Module* m, PinEvent pev, u32 pgid) { + static const QSet groupEvents = { PinEvent::GroupCreate, + PinEvent::GroupReorder, + PinEvent::GroupRename, + PinEvent::GroupTypeChange, + PinEvent::GroupDirChange, + PinEvent::GroupDelete}; Q_UNUSED(pev); Q_UNUSED(pgid); if ((int)m->get_id() != mModuleId) return; @@ -519,7 +525,7 @@ namespace hal int pinRow = -1; - if (pev < PinEvent::PinCreate) + if (groupEvents.contains(pev)) { // group event ptiGroup = mIdToGroupItem.value(pgid); diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index 903102888b9..05d23fdd293 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -52,6 +52,11 @@ namespace hal return pg.first->get_start_index() - row; } + uint qHash(PinEvent pev) + { + return (uint) pev; + } + QString generateGroupName(const Module* mod, const ModulePin* pin) { QString baseName = QString::fromStdString(pin->get_name()); diff --git a/src/netlist/gate_library/enums/pin_event.cpp b/src/netlist/gate_library/enums/pin_event.cpp new file mode 100644 index 00000000000..73bf9f001e4 --- /dev/null +++ b/src/netlist/gate_library/enums/pin_event.cpp @@ -0,0 +1,102 @@ +#include "hal_core/netlist/gate_library/enums/pin_event.h" +#include "hal_core/netlist/event_system/event_handler.h" +#include "hal_core/netlist/module.h" +#include +#include + +namespace hal { + + template<> + std::map EnumStrings::data = { + {PinEvent::unknown, "unknown"}, + {PinEvent::GroupCreate, "GroupCreate"}, + {PinEvent::GroupReorder, "GroupReorder"}, + {PinEvent::GroupRename, "GroupRename"}, + {PinEvent::GroupTypeChange, "GroupTypeChange"}, + {PinEvent::GroupDirChange, "GroupDirChange"}, + {PinEvent::GroupDelete, "GroupDelete"}, + {PinEvent::PinCreate, "PinCreate"}, + {PinEvent::PinReorder, "PinReorder"}, + {PinEvent::PinAssignToGroup, "PinAssignToGroup"}, + {PinEvent::PinRename, "PinRename"}, + {PinEvent::PinTypeChange, "PinTypeChange"}, + {PinEvent::PinDirChange, "PinDirChange"}, + {PinEvent::PinDelete, "PinDelete"} + }; + + std::unordered_map PinChangedEvent::s_event_stack; + + PinChangedEvent::PinChangedEvent(Module* m, PinEvent pev, u32 id) + : m_module(m), m_event(pev), m_id(id) + {;} + + void PinChangedEvent::send() + { + auto it = s_event_stack.find(m_module); + if (it == s_event_stack.end()) + { + // not stacked, send event immediately + m_module->get_event_handler()->notify(ModuleEvent::event::pin_changed, m_module, associated_data()); + return; + } + + // put event on stack to emit it later + it->second->push_back(*this); + } + + Module* PinChangedEvent::get_module() const + { + return m_module; + } + + u32 PinChangedEvent::associated_data() + { + return (m_id << 4) | (((u32)m_event)&0xF); + } + + bool pin_event_order(const PinChangedEvent& a, const PinChangedEvent& b) + { + if (a.m_event < b.m_event) return true; + if (a.m_event > b.m_event) return false; + return a.m_idget_event_handler()->notify(ModuleEvent::event::pin_changed, m, it->associated_data()); + } + + PinChangedEventScope::PinChangedEventScope(Module* m) + : m_module(m) + { + auto it = PinChangedEvent::s_event_stack.find(m); + if (it == PinChangedEvent::s_event_stack.end()) + PinChangedEvent::s_event_stack[m] = new PinChangedEvent::EventStack; + else + ++it->second->m_count; + } + + PinChangedEventScope::~PinChangedEventScope() + { + auto it = PinChangedEvent::s_event_stack.find(m_module); + assert(it != PinChangedEvent::s_event_stack.end()); + if (it->second->m_count > 0) + --it->second->m_count; + else + { + delete it->second; + PinChangedEvent::s_event_stack.erase(it); + } + } + + void PinChangedEventScope::send_events() + { + auto it = PinChangedEvent::s_event_stack.find(m_module); + assert(it != PinChangedEvent::s_event_stack.end()); + if (it->second->m_count > 0) // do not send yet + return; + it->second->send_events(m_module); + } +} diff --git a/src/netlist/gate_library/enums/pin_type.cpp b/src/netlist/gate_library/enums/pin_type.cpp index b9e9c01a296..091c0f98236 100644 --- a/src/netlist/gate_library/enums/pin_type.cpp +++ b/src/netlist/gate_library/enums/pin_type.cpp @@ -19,22 +19,4 @@ namespace hal {PinType::select, "select"}, {PinType::carry, "carry"}, {PinType::sum, "sum"}}; - - template<> - std::map EnumStrings::data = { - {PinEvent::unknown, "unknown"}, - {PinEvent::GroupCreate, "GroupCreate"}, - {PinEvent::GroupReorder, "GroupReorder"}, - {PinEvent::GroupRename, "GroupRename"}, - {PinEvent::GroupTypeChange, "GroupTypeChange"}, - {PinEvent::GroupDirChange, "GroupDirChange"}, - {PinEvent::GroupDelete, "GroupDelete"}, - {PinEvent::PinCreate, "PinCreate"}, - {PinEvent::PinReorder, "PinReorder"}, - {PinEvent::PinAssignToGroup, "PinAssignToGroup"}, - {PinEvent::PinRename, "PinRename"}, - {PinEvent::PinTypeChange, "PinTypeChange"}, - {PinEvent::PinDirChange, "PinDirChange"}, - {PinEvent::PinDelete, "PinDelete"} - }; } diff --git a/src/netlist/module.cpp b/src/netlist/module.cpp index af1e0a16e23..0226d386288 100644 --- a/src/netlist/module.cpp +++ b/src/netlist/module.cpp @@ -660,13 +660,13 @@ namespace hal { m_output_nets.insert(net); pin->set_direction(PinDirection::inout); - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); + PinChangedEvent(this,PinEvent::PinTypeChange,pin->get_id()).send(); } else if (direction == PinDirection::output) { m_input_nets.insert(net); pin->set_direction(PinDirection::inout); - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); + PinChangedEvent(this,PinEvent::PinTypeChange,pin->get_id()).send(); } } else @@ -687,7 +687,7 @@ namespace hal m_input_nets.insert(net); m_output_nets.erase(net); pin->set_direction(PinDirection::input); - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); + PinChangedEvent(this,PinEvent::PinTypeChange,pin->get_id()).send(); } } else @@ -709,7 +709,7 @@ namespace hal m_output_nets.insert(net); m_input_nets.erase(net); pin->set_direction(PinDirection::output); - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); + PinChangedEvent(this,PinEvent::PinTypeChange,pin->get_id()).send(); } } else @@ -851,13 +851,13 @@ namespace hal else { // pin assigned to new group OK - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupCreate,group_res.get()->get_id())); + PinChangedEvent(this,PinEvent::GroupCreate,group_res.get()->get_id()).send(); } } } else { - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinCreate,pin_res.get()->get_id())); + PinChangedEvent(this,PinEvent::PinCreate,pin_res.get()->get_id()).send(); } return pin_res; } @@ -1102,7 +1102,7 @@ namespace hal m_pin_names_map.erase(old_name); pin->set_name(new_name); m_pin_names_map[new_name] = pin; - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinRename,pin->get_id())); + PinChangedEvent(this,PinEvent::PinRename,pin->get_id()).send(); } return true; @@ -1125,7 +1125,7 @@ namespace hal if (pin->get_type() != new_type) { pin->set_type(new_type); - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinTypeChange,pin->get_id())); + PinChangedEvent(this,PinEvent::PinTypeChange,pin->get_id()).send(); } return true; @@ -1170,7 +1170,7 @@ namespace hal m_pin_group_names_map.erase(old_name); pin_group->set_name(new_name); m_pin_group_names_map[new_name] = pin_group; - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupRename,pin_group->get_id())); + PinChangedEvent(this,PinEvent::GroupRename,pin_group->get_id()).send(); } return true; @@ -1194,7 +1194,7 @@ namespace hal if (pin_group->get_type() != new_type) { pin_group->set_type(new_type); - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupTypeChange,pin_group->get_id())); + PinChangedEvent(this,PinEvent::GroupTypeChange,pin_group->get_id()).send(); } return true; } @@ -1221,7 +1221,7 @@ namespace hal if (pin_group->get_direction() != new_direction) { pin_group->set_direction(new_direction); - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupTypeChange,pin_group->get_id())); + PinChangedEvent(this,PinEvent::GroupTypeChange,pin_group->get_id()).send(); } return true; } @@ -1235,6 +1235,8 @@ namespace hal u32 start_index, bool delete_empty_groups) { + PinChangedEventScope scope(this); + if (name.empty()) { return ERR("could not create pin group for module '" + m_name + "' with ID " + std::to_string(m_id) + ": empty string passed as name"); @@ -1259,7 +1261,8 @@ namespace hal } } - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupCreate,pin_group->get_id())); + PinChangedEvent(this,PinEvent::GroupCreate,pin_group->get_id()).send(); + scope.send_events(); return OK(pin_group); } @@ -1271,7 +1274,7 @@ namespace hal bool Module::delete_pin_group(PinGroup* pin_group) { - std::vector distribute_events; + PinChangedEventScope scope(this); if (pin_group == nullptr) { log_warning("module", "could not delete pin group from module '{}' with ID {}: pin group is a 'nullptr'", m_name, m_id); @@ -1294,8 +1297,8 @@ namespace hal { return false; } - distribute_events.push_back(pinevent_associated_data(PinEvent::GroupCreate,res.get()->get_id())); - distribute_events.push_back(pinevent_associated_data(PinEvent::PinAssignToGroup,pin->get_id())); + PinChangedEvent(this,PinEvent::GroupCreate,res.get()->get_id()).send(); + PinChangedEvent(this,PinEvent::PinAssignToGroup,pin->get_id()).send(); } u32 pin_group_id_to_delete = pin_group->get_id(); @@ -1305,9 +1308,8 @@ namespace hal return false; } - for (u32 dist_ev : distribute_events) - m_event_handler->notify(ModuleEvent::event::pin_changed, this, dist_ev); - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupDelete,pin_group_id_to_delete)); + PinChangedEvent(this,PinEvent::GroupDelete,pin_group_id_to_delete).send(); + scope.send_events(); return true; } @@ -1350,13 +1352,13 @@ namespace hal m_pin_groups_ordered.splice(dst_it, m_pin_groups_ordered, src_it); } - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupReorder,pin_group->get_id())); + PinChangedEvent(this,PinEvent::GroupReorder,pin_group->get_id()).send(); return true; } bool Module::assign_pin_to_group(PinGroup* pin_group, ModulePin* pin, bool delete_empty_groups) { - u32 pin_group_id_to_delete = 0; + PinChangedEventScope scope(this); if (pin_group == nullptr) { @@ -1398,7 +1400,7 @@ namespace hal if (delete_empty_groups && pg->empty()) { - pin_group_id_to_delete = pg->get_id(); + PinChangedEvent(this,PinEvent::GroupDelete,pg->get_id()).send(); if (!delete_pin_group_internal(pg)) { log_warning("module", @@ -1416,9 +1418,8 @@ namespace hal return false; } - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinAssignToGroup,pin->get_id())); - if (pin_group_id_to_delete) - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupDelete,pin_group_id_to_delete)); + PinChangedEvent(this,PinEvent::PinAssignToGroup,pin->get_id()).send(); + scope.send_events(); return true;; } @@ -1461,7 +1462,7 @@ namespace hal return false; } - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinReorder,pin->get_id())); + PinChangedEvent(this,PinEvent::PinReorder,pin->get_id()).send(); return true; } @@ -1556,13 +1557,13 @@ namespace hal } } - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupCreate,pin->get_group().first->get_id())); + PinChangedEvent(this,PinEvent::GroupCreate,pin->get_group().first->get_id()).send(); return OK(pin); } bool Module::remove_pin_net(Net* net) { - u32 pin_group_id_to_delete = 0; + PinChangedEventScope scope(this); auto pin = get_pin_by_net(net); if (pin == nullptr) { @@ -1581,7 +1582,7 @@ namespace hal if (pin_group->empty()) { - pin_group_id_to_delete = pin_group->get_id(); + PinChangedEvent(this,PinEvent::GroupDelete,pin_group->get_id()).send(); if (!delete_pin_group_internal(pin_group)) { log_warning("module", "could not remove pin '{}' with ID {} from net '{}' with ID {}: failed to delete pin group '{}' with ID {}", pin->get_name(), pin->get_id(), net->get_name(), net->get_id(), pin_group->get_name(), pin_group->get_id()); @@ -1598,9 +1599,8 @@ namespace hal return false; } - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::PinDelete,pin_id_to_delete)); - if (pin_group_id_to_delete) - m_event_handler->notify(ModuleEvent::event::pin_changed, this, pinevent_associated_data(PinEvent::GroupDelete,pin_group_id_to_delete)); + PinChangedEvent(this,PinEvent::PinDelete,pin_id_to_delete).send(); + scope.send_events(); return true; } @@ -1738,9 +1738,8 @@ namespace hal return true; } - u32 Module::pinevent_associated_data(PinEvent pev, u32 id) + EventHandler* Module::get_event_handler() const { - return (id << 4) | (((u32)pev)&0xF); + return m_event_handler; } - } // namespace hal From 152cc46cf860275fd0f17f70b4090b771026aced Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 2 Oct 2023 16:00:33 +0200 Subject: [PATCH 19/66] minor fixes --- include/hal_core/netlist/module.h | 2 +- include/hal_core/netlist/pins/base_pin.h | 11 ---- .../include/gui/user_action/action_pingroup.h | 25 ++++--- .../module_ports_tree.cpp | 2 +- .../module_details_widget/port_tree_model.cpp | 6 +- .../gui/src/user_action/action_pingroup.cpp | 66 +++++++++---------- 6 files changed, 50 insertions(+), 62 deletions(-) diff --git a/include/hal_core/netlist/module.h b/include/hal_core/netlist/module.h index 361571a27cb..7f4c2bd1de6 100644 --- a/include/hal_core/netlist/module.h +++ b/include/hal_core/netlist/module.h @@ -530,7 +530,7 @@ namespace hal * * @param[in] pin_group - The pin group to be moved. * @param[in] new_index - The index to which the pin group is moved. - * @returns Ok on success, an error message otherwise. + * @returns true on success, false message otherwise. */ bool move_pin_group(PinGroup* pin_group, u32 new_index); diff --git a/include/hal_core/netlist/pins/base_pin.h b/include/hal_core/netlist/pins/base_pin.h index b168aeb1655..855db0470bd 100644 --- a/include/hal_core/netlist/pins/base_pin.h +++ b/include/hal_core/netlist/pins/base_pin.h @@ -100,17 +100,6 @@ namespace hal m_name = name; } - int get_pos() const - { - auto g = get_group(); - int i = 0; - for (auto p : g.first->get_pins()) - { - if (p == this) return i; - ++i; - } - return -1; - } /** * Get the name of the pin. * diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index 375cc0ff2a1..e9b31c54f1f 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -35,8 +35,8 @@ namespace hal { Q_OBJECT public: - enum Type { None, GroupCreate, GroupDelete, GroupMove, GroupRename, GroupTypechange, GroupDirection, - PinAsignGroup, PinRename, PinTypechange, PinDirection, PinSetindex, MaxAction }; + enum Type { None, GroupCreate, GroupDelete, GroupMoveToRow, GroupRename, GroupTypeChange, GroupDirChange, + PinAsignToGroup, PinRename, PinTypeChange, PinDirChange, PinMoveToRow, MaxAction }; Q_ENUM(Type); public: @@ -67,28 +67,28 @@ namespace hal * ID will be used internally for subsequent commands related to crated group * name : name of group * value : start index, assume ascending - * negative value: descending order starting with (-value) + * negative value: descending order starting with (-value-1) * * GroupDelete * ID : ID of group to delete * - * GroupMove + * GroupMoveToRow * ID : ID of group to move - * value : new position in vector of pin groups + * value : row to which group get moved within vector of pin groups * * GroupRename * ID : ID of group to rename * name : new name * - * GroupTypechange + * GroupTypeChange * ID : ID of group to modifiy * value : (int) PinType as of hal_core/netlist/gate_library/enums/pin_type.h * - * GroupDirection + * GroupDirChange * ID : ID of group to modifiy * value : (int) PinDirection as of hal_core/netlist/gate_library/enums/pin_direction.h * - * PinAsignGroup + * PinAsignToGroup * ID : ID of pin * value : ID of group, might be negative if group recently created * @@ -96,18 +96,17 @@ namespace hal * ID : ID of pin to rename * name : new name * - * PinTypechange + * PinTypeChange * ID : ID of pin to modify * value : (int) PinType * - * PinDirection + * PinDirChange * ID : ID of pin to modify * value : (int) PinDirection * - * PinSetindex + * PinMoveToRow * ID : ID of pin - * value : new index in pingroup. Calculated from row by - * index = startindex + a * row with a=1 for ascending, a=-1 for descending + * value : row to which pin gets moved in pingroup */ class ActionPingroup : public UserAction { diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp index cd605847317..366b8fb1d09 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp @@ -211,7 +211,7 @@ namespace hal { PinType ptype = enum_from_string(cbd.textValue().toStdString(),PinType::none); - ActionPingroup* act = new ActionPingroup(PinActionType::PinTypechange,pin->get_id(),"",(int)ptype); + ActionPingroup* act = new ActionPingroup(PinActionType::PinTypeChange,pin->get_id(),"",(int)ptype); act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); act->exec(); } diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index b8ab9496658..d9afcee3858 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -695,14 +695,14 @@ namespace hal bool bottomEdge = row == mRootItem->getChildCount(); auto desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; - ActionPingroup* act = new ActionPingroup(PinActionType::GroupMove,droppedGroup->id(),"",desiredIdx); + ActionPingroup* act = new ActionPingroup(PinActionType::GroupMoveToRow,droppedGroup->id(),"",desiredIdx); act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); act->exec(); } void ModulePinsTreeModel::dndPinOnGroup(PortTreeItem *droppedPin, BaseTreeItem *onDroppedGroup) { - ActionPingroup* act = new ActionPingroup(PinActionType::PinAsignGroup,droppedPin->id(),"",static_cast(onDroppedGroup)->id()); + ActionPingroup* act = new ActionPingroup(PinActionType::PinAsignToGroup,droppedPin->id(),"",static_cast(onDroppedGroup)->id()); act->setObject(UserActionObject(mModuleId,UserActionObjectType::Module)); act->exec(); } @@ -717,7 +717,7 @@ namespace hal bool bottomEdge = row == onDroppedParent->getChildCount(); desiredIdx = bottomEdge ? row-1 : row; if(ownRow < row && !bottomEdge) desiredIdx--; // insert item here - act = new ActionPingroup(PinActionType::PinSetindex,droppedPin->id(),"",desiredIdx); // TODO : start_index, descending + act = new ActionPingroup(PinActionType::PinMoveToRow,droppedPin->id(),"",desiredIdx); // TODO : start_index, descending } else { diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index 05d23fdd293..892f0d85087 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -28,7 +28,7 @@ namespace hal std::cerr << " grp: " << pg->get_id() << (pg->is_ascending()?" asc ": " des ") << pg->get_start_index() << " <" << pg->get_name() << ">\n"; for (ModulePin* pin : pg->get_pins()) - std::cerr << " pin: " << pin->get_id() << " inx:" << pin->get_group().second << " pos:" << pin->get_pos() << " row:" + std::cerr << " pin: " << pin->get_id() << " inx:" << pin->get_group().second << " row:" << pinIndex2Row(pin,pin->get_group().second) << " <" << pin->get_name() << ">\n"; } std::cerr << "-------------" << std::endl; @@ -89,13 +89,13 @@ namespace hal bool PinActionType::useExistingGroup(PinActionType::Type tp) { - static const QSet types = {GroupDelete, GroupMove, GroupRename, GroupTypechange, GroupDirection}; + static const QSet types = {GroupDelete, GroupMoveToRow, GroupRename, GroupTypeChange, GroupDirChange}; return types.contains(tp); } bool PinActionType::useExistingPin(PinActionType::Type tp) { - static const QSet types = {PinAsignGroup, PinRename, PinTypechange, PinDirection, PinSetindex}; + static const QSet types = {PinAsignToGroup, PinRename, PinTypeChange, PinDirChange, PinMoveToRow}; return types.contains(tp); } @@ -206,7 +206,7 @@ namespace hal QHash*,int> remainingPins; for (const AtomicAction& aa : mPinActions) { - if (aa.mType != PinActionType::PinAsignGroup) + if (aa.mType != PinActionType::PinAsignToGroup) continue; ModulePin* pin = mParentModule->get_pin_by_id(aa.mId); PinGroup* pgroup = pin->get_group().first; @@ -232,11 +232,11 @@ namespace hal { const GroupRestore& gr = it.value(); restoreActions.append(AtomicAction(PinActionType::GroupCreate,gr.mId,gr.mName,gr.mStartIndex)); - restoreActions.append(AtomicAction(PinActionType::GroupMove,gr.mId,"",gr.mRow)); + restoreActions.append(AtomicAction(PinActionType::GroupMoveToRow,gr.mId,"",gr.mRow)); if (gr.mType != PinType::none) - restoreActions.append(AtomicAction(PinActionType::GroupTypechange,gr.mId,"",(int)gr.mType)); + restoreActions.append(AtomicAction(PinActionType::GroupTypeChange,gr.mId,"",(int)gr.mType)); if (gr.mDirection != PinDirection::none) - restoreActions.append(AtomicAction(PinActionType::GroupDirection,gr.mId,"",(int)gr.mDirection)); + restoreActions.append(AtomicAction(PinActionType::GroupDirChange,gr.mId,"",(int)gr.mDirection)); } if (!restoreActions.isEmpty()) { @@ -324,7 +324,7 @@ namespace hal if (aa.mValue < 0) { ascending = false; - startIndex = -aa.mValue; + startIndex = -aa.mValue-1; } if (aa.mId > 0) { @@ -350,7 +350,7 @@ namespace hal case PinActionType::GroupDelete: { int v = pgroup->get_start_index(); - if (!pgroup->is_ascending()) v = -v; + if (!pgroup->is_ascending()) v = -v-1; u32 id = pgroup->get_id(); int ptype = (int) pgroup->get_type(); int pdir = (int) pgroup->get_direction(); @@ -358,15 +358,15 @@ namespace hal if (!mParentModule->delete_pin_group(pgroup)) return false; addUndoAction(PinActionType::GroupCreate,id,name,v); - addUndoAction(PinActionType::GroupTypechange,id,"",ptype); - addUndoAction(PinActionType::GroupDirection,id,"",pdir); + addUndoAction(PinActionType::GroupTypeChange,id,"",ptype); + addUndoAction(PinActionType::GroupDirChange,id,"",pdir); break; } - case PinActionType::GroupMove: + case PinActionType::GroupMoveToRow: { int inx = pinGroupRow(mParentModule,pgroup); if (inx < 0) return false; - addUndoAction(PinActionType::GroupMove,pgroup->get_id(),"",inx); + addUndoAction(PinActionType::GroupMoveToRow,pgroup->get_id(),"",inx); if (!mParentModule->move_pin_group(pgroup,aa.mValue)) return false; break; @@ -376,19 +376,19 @@ namespace hal if (!mParentModule->set_pin_group_name(pgroup,aa.mName.toStdString())) return false; break; - case PinActionType::GroupTypechange: - addUndoAction(PinActionType::GroupTypechange,pgroup->get_id(),"",(int)pgroup->get_type()); + case PinActionType::GroupTypeChange: + addUndoAction(PinActionType::GroupTypeChange,pgroup->get_id(),"",(int)pgroup->get_type()); if (!mParentModule->set_pin_group_type(pgroup, (PinType) aa.mValue)) return false; break; - case PinActionType::GroupDirection: - addUndoAction(PinActionType::GroupDirection,pgroup->get_id(),"",(int)pgroup->get_direction()); + case PinActionType::GroupDirChange: + addUndoAction(PinActionType::GroupDirChange,pgroup->get_id(),"",(int)pgroup->get_direction()); if (!mParentModule->set_pin_group_direction(pgroup, (PinDirection) aa.mValue)) return false; break; - case PinActionType::PinAsignGroup: - addUndoAction(PinActionType::PinAsignGroup,aa.mId,"",pin->get_group().first->get_id()); - addUndoAction(PinActionType::PinSetindex,aa.mId,"",pinIndex2Row(pin,pin->get_group().second)); + case PinActionType::PinAsignToGroup: + addUndoAction(PinActionType::PinAsignToGroup,aa.mId,"",pin->get_group().first->get_id()); + addUndoAction(PinActionType::PinMoveToRow,aa.mId,"",pinIndex2Row(pin,pin->get_group().second)); mPinsMoved.insert(aa.mId); pgroup = getGroup(aa.mValue); if (!pgroup) return false; @@ -404,14 +404,14 @@ namespace hal if (!mParentModule->set_pin_name(pin, aa.mName.toStdString())) return false; break; - case PinActionType::PinTypechange: - addUndoAction(PinActionType::PinTypechange,aa.mId,"",(int)pin->get_type()); + case PinActionType::PinTypeChange: + addUndoAction(PinActionType::PinTypeChange,aa.mId,"",(int)pin->get_type()); if (!mParentModule->set_pin_type(pin, (PinType) aa.mValue)) return false; break; - case PinActionType::PinSetindex: + case PinActionType::PinMoveToRow: if (!mPinsMoved.contains(aa.mId)) - addUndoAction(PinActionType::PinSetindex,aa.mId,"",pinIndex2Row(pin,pin->get_group().second)); + addUndoAction(PinActionType::PinMoveToRow,aa.mId,"",pinIndex2Row(pin,pin->get_group().second)); pgroup = pin->get_group().first; if (!mParentModule->move_pin_within_group(pgroup,pin,pinRow2Index(pin,aa.mValue))) { @@ -436,11 +436,11 @@ namespace hal for (u32 pinId : pinIds) { if (retval) - retval->mPinActions.append(AtomicAction(PinActionType::PinAsignGroup,pinId,"",grpId)); + retval->mPinActions.append(AtomicAction(PinActionType::PinAsignToGroup,pinId,"",grpId)); else - retval = new ActionPingroup(PinActionType::PinAsignGroup,pinId,"",grpId); + retval = new ActionPingroup(PinActionType::PinAsignToGroup,pinId,"",grpId); if (pinRow >= 0) - retval->mPinActions.append(AtomicAction(PinActionType::PinSetindex,pinId,"",pinRow++)); + retval->mPinActions.append(AtomicAction(PinActionType::PinMoveToRow,pinId,"",pinRow++)); } retval->setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); return retval; @@ -462,15 +462,15 @@ namespace hal ModulePin* pin = m->get_pin_by_id(pinIds.first()); if (pin) { - retval->mPinActions.append(AtomicAction(PinActionType::GroupDirection,vid,"",(int)pin->get_direction())); - retval->mPinActions.append(AtomicAction(PinActionType::GroupTypechange,vid,"",(int)pin->get_type())); + retval->mPinActions.append(AtomicAction(PinActionType::GroupDirChange,vid,"",(int)pin->get_direction())); + retval->mPinActions.append(AtomicAction(PinActionType::GroupTypeChange,vid,"",(int)pin->get_type())); } } for (u32 pinId : pinIds) - retval->mPinActions.append(AtomicAction(PinActionType::PinAsignGroup,pinId,"",vid)); + retval->mPinActions.append(AtomicAction(PinActionType::PinAsignToGroup,pinId,"",vid)); if (grpRow >= 0) - retval->mPinActions.append(AtomicAction(PinActionType::GroupMove,vid,"",grpRow)); + retval->mPinActions.append(AtomicAction(PinActionType::GroupMoveToRow,vid,"",grpRow)); retval->setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); return retval; } @@ -502,7 +502,7 @@ namespace hal retval->mPinActions.append(AtomicAction(PinActionType::GroupCreate,vid,name)); else retval = new ActionPingroup(PinActionType::GroupCreate,vid,name); - retval->mPinActions.append(AtomicAction(PinActionType::PinAsignGroup,pinId,"",vid)); + retval->mPinActions.append(AtomicAction(PinActionType::PinAsignToGroup,pinId,"",vid)); --vid; } retval->setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); @@ -517,6 +517,6 @@ namespace hal mDirection(pgroup->get_direction()), mType(pgroup->get_type()) { - if (!pgroup->is_ascending()) mStartIndex = -mStartIndex; + if (!pgroup->is_ascending()) mStartIndex = -mStartIndex-1; } } From bd3aca7eb1e9fd4dd89e0d0732877f8adbca0f7b Mon Sep 17 00:00:00 2001 From: joern274 Date: Tue, 3 Oct 2023 17:03:08 +0200 Subject: [PATCH 20/66] Fix module and gate-type tests --- include/hal_core/netlist/pins/pin_group.h | 32 ++++++++----- .../netlist_modification_decorator.cpp | 2 +- src/netlist/gate_library/gate_type.cpp | 33 ++++++++++---- src/netlist/module.cpp | 32 +++++++++---- tests/netlist/module.cpp | 45 +++++++++++++------ 5 files changed, 101 insertions(+), 43 deletions(-) diff --git a/include/hal_core/netlist/pins/pin_group.h b/include/hal_core/netlist/pins/pin_group.h index 2e125ff46b9..31f0f526cab 100644 --- a/include/hal_core/netlist/pins/pin_group.h +++ b/include/hal_core/netlist/pins/pin_group.h @@ -426,18 +426,20 @@ namespace hal * Remove a pin from the pin group. * * @param[in] pin - The pin to remove. - * @returns Ok on success, an error message otherwise. + * @returns true on success, false otherwise. */ - Result remove_pin(T* pin) + bool remove_pin(T* pin) { if (pin == nullptr) { - return ERR("'nullptr' given instead of a pin when trying to remove pin from pin group '" + m_name + "' with ID " + std::to_string(m_id)); + log_warning("pin_group", "'nullptr' given instead of a pin when trying to remove pin from pin group '{}' with ID {}.", m_name, m_id); + return false; } if (pin->m_group.first != this) { - return ERR("pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " does not belong to pin group '" + m_name + "' with ID " + std::to_string(m_id)); + log_warning("pin_group", "pin '{}' with ID {} does not belong to pin group '{}' with ID {}.", pin->get_name(), pin->get_id(), m_name, m_id); + return false; } i32 index = pin->m_group.second; @@ -454,16 +456,24 @@ namespace hal } else { - auto it = std::next(m_pins.begin(), m_start_index - index); - it = m_pins.erase(it); - for (; it != m_pins.end(); it++) + if (m_pins.size()==1) { - std::get<1>((*it)->m_group)++; + m_pins.clear(); + m_next_index++; } - m_next_index++; - } + else + { + auto it = m_pins.begin(); + for (int i=m_start_index; i>index;i--) + { + std::get<1>((*(it++))->m_group)--; + } + m_pins.erase(it); + --m_start_index; + } + } - return OK({}); + return true; } /** diff --git a/src/netlist/decorators/netlist_modification_decorator.cpp b/src/netlist/decorators/netlist_modification_decorator.cpp index 73bd446de32..102ac6087c7 100644 --- a/src/netlist/decorators/netlist_modification_decorator.cpp +++ b/src/netlist/decorators/netlist_modification_decorator.cpp @@ -292,7 +292,7 @@ namespace hal // remove pin from current pin group auto current_pin_group = pin->get_group().first; // This get is safe, since we make sure that the pin is a valid pointer and part of the group - current_pin_group->remove_pin(pin).get(); + current_pin_group->remove_pin(pin); // delete old pin group incase it is now empty if (current_pin_group->get_pins().empty()) diff --git a/src/netlist/gate_library/gate_type.cpp b/src/netlist/gate_library/gate_type.cpp index 3a4050ed2f4..7c23a250fd2 100644 --- a/src/netlist/gate_library/gate_type.cpp +++ b/src/netlist/gate_library/gate_type.cpp @@ -370,6 +370,12 @@ namespace hal } PinGroup* pin_group; + if (!ascending && !pins.empty()) + { + // compensate for shifting the start index + start_index -= (pins.size()-1); + } + if (auto res = create_pin_group_internal(id, name, direction, type, ascending, start_index); res.is_error()) { return ERR_APPEND(res.get_error(), "could not create pin group '" + name + "' for gate type '" + m_name + "' with ID " + std::to_string(m_id)); @@ -379,13 +385,23 @@ namespace hal pin_group = res.get(); } - for (auto* pin : pins) + if (ascending) { - if (auto res = assign_pin_to_group(pin_group, pin, delete_empty_groups); res.is_error()) - { - assert(delete_pin_group(pin_group)); - return ERR(res.get_error()); - } + for (auto it = pins.begin(); it != pins.end(); ++it) + if (auto res = assign_pin_to_group(pin_group, *it, delete_empty_groups); res.is_error()) + { + assert(delete_pin_group(pin_group)); + return ERR(res.get_error()); + } + } + else + { + for (auto it = pins.rbegin(); it != pins.rend(); ++it) + if (auto res = assign_pin_to_group(pin_group, *it, delete_empty_groups); res.is_error()) + { + assert(delete_pin_group(pin_group)); + return ERR(res.get_error()); + } } return OK(pin_group); @@ -488,10 +504,9 @@ namespace hal if (PinGroup* pg = pin->get_group().first; pg != nullptr) { // remove from old group and potentially delete old group if empty - if (auto res = pg->remove_pin(pin); res.is_error()) + if (!pg->remove_pin(pin)) { - return ERR_APPEND(res.get_error(), - "could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " + return ERR("could not assign pin '" + pin->get_name() + "' with ID " + std::to_string(pin->get_id()) + " to pin group '" + pin_group->get_name() + "' with ID " + std::to_string(pin_group->get_id()) + " of gate type '" + m_name + "' with ID " + std::to_string(m_id) + ": unable to remove pin from pin group '" + pg->get_name() + "' with ID " + std::to_string(pg->get_id())); } diff --git a/src/netlist/module.cpp b/src/netlist/module.cpp index 0226d386288..79fc829e7c3 100644 --- a/src/netlist/module.cpp +++ b/src/netlist/module.cpp @@ -1243,6 +1243,12 @@ namespace hal } PinGroup* pin_group; + if (!ascending && !pins.empty()) + { + // compensate for shifting the start index + start_index -= (pins.size()-1); + } + if (auto res = create_pin_group_internal(id, name, direction, type, ascending, start_index); res.is_error()) { return ERR_APPEND(res.get_error(), "could not create pin group '" + name + "' for module '" + m_name + "' with ID " + std::to_string(m_id)); @@ -1252,13 +1258,23 @@ namespace hal pin_group = res.get(); } - for (auto* pin : pins) + if (ascending) { - if (!assign_pin_to_group(pin_group, pin, delete_empty_groups)) - { - assert(delete_pin_group(pin_group)); - return ERR("Assign pin to group failed."); - } + for (auto it = pins.begin(); it != pins.end(); ++it) + if (!assign_pin_to_group(pin_group, *it, delete_empty_groups)) + { + assert(delete_pin_group(pin_group)); + return ERR("Assign pin to group failed."); + } + } + else + { + for (auto it = pins.rbegin(); it != pins.rend(); ++it) + if (!assign_pin_to_group(pin_group, *it, delete_empty_groups)) + { + assert(delete_pin_group(pin_group)); + return ERR("Assign pin to group failed."); + } } PinChangedEvent(this,PinEvent::GroupCreate,pin_group->get_id()).send(); @@ -1390,7 +1406,7 @@ namespace hal if (PinGroup* pg = pin->get_group().first; pg != nullptr) { // remove from old group and potentially delete old group if empty - if (auto res = pg->remove_pin(pin); res.is_error()) + if (!pg->remove_pin(pin)) { log_warning("module", "could not assign pin '{}' with ID {} to pin group '{}' with ID {} of module '{}' with ID {}: unable to remove pin from pin group '{}' with ID {}", @@ -1574,7 +1590,7 @@ namespace hal PinGroup* pin_group = pin->get_group().first; assert(pin_group != nullptr); - if (auto res = pin_group->remove_pin(pin); res.is_error()) + if (!pin_group->remove_pin(pin)) { log_warning("module", "could not remove pin '{}' with ID {} from net '{}' with ID {}: failed to remove pin from pin group '{}' with ID {}", pin->get_name(), pin->get_id(), net->get_name(), net->get_id(), pin_group->get_name(), pin_group->get_id()); return false; diff --git a/tests/netlist/module.cpp b/tests/netlist/module.cpp index e8dd56823e4..5e4dc48c573 100644 --- a/tests/netlist/module.cpp +++ b/tests/netlist/module.cpp @@ -1110,6 +1110,9 @@ namespace hal { // create input pin group auto res = m_1->create_pin_group("I", {in_pin_0, in_pin_1}, PinDirection::input, PinType::none, false, 2); ASSERT_TRUE(res.is_ok()); + // descending group start index 2 + // in_pin_0 "I0" index 2 + // in_pin_1 "I1" index 1 PinGroup* in_group = res.get(); ASSERT_NE(in_group, nullptr); EXPECT_EQ(m_1->get_pin_groups().size(), 3); @@ -1138,6 +1141,9 @@ namespace hal { // move pins within group EXPECT_TRUE(m_1->move_pin_within_group(in_group, in_pin_1, 2)); + // descending group start index 2 + // in_pin_1 "I1" index 2 + // in_pin_0 "I0" index 1 EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group, i32(1))); EXPECT_EQ(in_group->get_index(in_pin_0).get(), 1); EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(2))); @@ -1145,34 +1151,45 @@ namespace hal { EXPECT_EQ(in_group->get_pin_at_index(1).get(), in_pin_0); EXPECT_EQ(in_group->get_pin_at_index(2).get(), in_pin_1); - EXPECT_TRUE(m_1->move_pin_within_group(in_group, in_pin_1, 1)); + EXPECT_TRUE(m_1->move_pin_within_group(in_group, in_pin_0, 2)); + // descending group start index 2 + // in_pin_0 "I0" index 2 + // in_pin_1 "I1" index 1 EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group, i32(2))); EXPECT_EQ(in_group->get_index(in_pin_0).get(), 2); EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(1))); EXPECT_EQ(in_group->get_index(in_pin_1).get(), 1); EXPECT_EQ(in_group->get_pin_at_index(2).get(), in_pin_0); - EXPECT_EQ(in_group->get_pin_at_index(1).get(), in_pin_1); + EXPECT_EQ(in_group->get_pin_at_index(1).get(), in_pin_1); // remove pin from group EXPECT_TRUE(m_1->remove_pin_from_group(in_group, in_pin_0)); - EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(2))); - EXPECT_EQ(in_group->get_index(in_pin_1).get(), 2); - EXPECT_EQ(in_group->get_pin_at_index(2).get(), in_pin_1); + // descending group start index 1 + // in_pin_1 "I1" index 1 + EXPECT_EQ(in_group->get_start_index(), 1); + EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(1))); + EXPECT_EQ(in_group->get_index(in_pin_1).get(), 1); + EXPECT_EQ(in_group->get_pin_at_index(1).get(), in_pin_1); EXPECT_EQ(in_pin_0->get_group().first->get_name(), in_pin_0->get_name()); EXPECT_EQ(in_pin_0->get_group().second, 0); + + // assign pin to group EXPECT_TRUE(m_1->assign_pin_to_group(in_group, in_pin_0)); + // descending group start index 2 + // in_pin_0 "I0" index 2 + // in_pin_1 "I1" index 1 EXPECT_EQ(in_group->size(), 2); - // assign pin to group + // assign same pin twice should not do anything EXPECT_TRUE(m_1->assign_pin_to_group(in_group, in_pin_0)); EXPECT_EQ(m_1->get_pin_groups().size(), 3); EXPECT_EQ(in_group->size(), 2); - EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group, i32(1))); - EXPECT_EQ(in_group->get_index(in_pin_0).get(), 1); - EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(2))); - EXPECT_EQ(in_group->get_index(in_pin_1).get(), 2); - EXPECT_EQ(in_group->get_pin_at_index(1).get(), in_pin_0); - EXPECT_EQ(in_group->get_pin_at_index(2).get(), in_pin_1); + EXPECT_EQ(in_pin_0->get_group(), std::pair(in_group, i32(2))); + EXPECT_EQ(in_group->get_index(in_pin_0).get(), 2); + EXPECT_EQ(in_pin_1->get_group(), std::pair(in_group, i32(1))); + EXPECT_EQ(in_group->get_index(in_pin_1).get(), 1); + EXPECT_EQ(in_group->get_pin_at_index(2).get(), in_pin_0); + EXPECT_EQ(in_group->get_pin_at_index(1).get(), in_pin_1); } } TEST_END @@ -1257,8 +1274,8 @@ namespace hal { std::make_tuple(ModuleEvent::event::submodule_added, test_mod, other_mod_sub->get_id()), std::make_tuple(ModuleEvent::event::submodule_removed, test_mod, other_mod_sub->get_id()), std::make_tuple(ModuleEvent::event::gate_assigned, test_mod, test_gate->get_id()), - std::make_tuple(ModuleEvent::event::pin_changed, test_mod, NO_DATA), - std::make_tuple(ModuleEvent::event::pin_changed, test_mod, NO_DATA), + std::make_tuple(ModuleEvent::event::pin_changed, test_mod, PinChangedEvent(test_mod,PinEvent::PinRename,3).associated_data()), + std::make_tuple(ModuleEvent::event::pin_changed, test_mod, PinChangedEvent(test_mod,PinEvent::PinRename,1).associated_data()), std::make_tuple(ModuleEvent::event::gate_removed, test_mod, test_gate->get_id()) }; From e9c95958abea7ae6fe9c2f9cb926fb4b29e61328 Mon Sep 17 00:00:00 2001 From: joern274 Date: Tue, 3 Oct 2023 19:23:05 +0200 Subject: [PATCH 21/66] HGL-writer test fixed --- .../src/gate_library_test_utils.cpp | 82 +++++++++++-------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/tests/test_utils/src/gate_library_test_utils.cpp b/tests/test_utils/src/gate_library_test_utils.cpp index 019ee656453..83d94cb8edd 100644 --- a/tests/test_utils/src/gate_library_test_utils.cpp +++ b/tests/test_utils/src/gate_library_test_utils.cpp @@ -424,41 +424,45 @@ namespace hal { const auto grp = res_grp.get(); - if (auto res = carry4->create_pin("DI(3)", PinDirection::input, PinType::none, false); res.is_error()) + std::list pins; + if (auto res = carry4->create_pin("DI(0)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("DI(2)", PinDirection::input, PinType::none, false); res.is_error()) + if (auto res = carry4->create_pin("DI(1)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("DI(1)", PinDirection::input, PinType::none, false); res.is_error()) + if (auto res = carry4->create_pin("DI(2)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("DI(0)", PinDirection::input, PinType::none, false); res.is_error()) + if (auto res = carry4->create_pin("DI(3)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } + + for (auto pin : pins) + grp->assign_pin(pin); } if (auto res_grp = carry4->create_pin_group("S", {}, PinDirection::input, PinType::none, false, 3); res_grp.is_error()) @@ -469,41 +473,45 @@ namespace hal { const auto grp = res_grp.get(); - if (auto res = carry4->create_pin("S(3)", PinDirection::input, PinType::none, false); res.is_error()) + std::list pins; + if (auto res = carry4->create_pin("S(0)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("S(2)", PinDirection::input, PinType::none, false); res.is_error()) + if (auto res = carry4->create_pin("S(1)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("S(1)", PinDirection::input, PinType::none, false); res.is_error()) + if (auto res = carry4->create_pin("S(2)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("S(0)", PinDirection::input, PinType::none, false); res.is_error()) + if (auto res = carry4->create_pin("S(3)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } + + for (auto pin : pins) + grp->assign_pin(pin); } if (auto res_grp = carry4->create_pin_group("CO", {}, PinDirection::output, PinType::carry, false, 3); res_grp.is_error()) @@ -514,41 +522,45 @@ namespace hal { const auto grp = res_grp.get(); - if (auto res = carry4->create_pin("CO(3)", PinDirection::output, PinType::carry, false); res.is_error()) + std::list pins; + if (auto res = carry4->create_pin("CO(0)", PinDirection::output, PinType::carry, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("CO(2)", PinDirection::output, PinType::carry, false); res.is_error()) + if (auto res = carry4->create_pin("CO(1)", PinDirection::output, PinType::carry, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("CO(1)", PinDirection::output, PinType::carry, false); res.is_error()) + if (auto res = carry4->create_pin("CO(2)", PinDirection::output, PinType::carry, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("CO(0)", PinDirection::output, PinType::carry, false); res.is_error()) + if (auto res = carry4->create_pin("CO(3)", PinDirection::output, PinType::carry, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } + + for (auto pin : pins) + grp->assign_pin(pin); } if (auto res_grp = carry4->create_pin_group("O", {}, PinDirection::output, PinType::none, false, 3); res_grp.is_error()) @@ -559,41 +571,45 @@ namespace hal { const auto grp = res_grp.get(); - if (auto res = carry4->create_pin("O(3)", PinDirection::output, PinType::none, false); res.is_error()) + std::list pins; + if (auto res = carry4->create_pin("O(0)", PinDirection::output, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("O(2)", PinDirection::output, PinType::none, false); res.is_error()) + if (auto res = carry4->create_pin("O(1)", PinDirection::output, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("O(1)", PinDirection::output, PinType::none, false); res.is_error()) + if (auto res = carry4->create_pin("O(2)", PinDirection::output, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } - if (auto res = carry4->create_pin("O(0)", PinDirection::output, PinType::none, false); res.is_error()) + if (auto res = carry4->create_pin("O(3)", PinDirection::output, PinType::none, false); res.is_error()) { return nullptr; } else { - grp->assign_pin(res.get()); + pins.push_front(res.get()); } + + for (auto pin : pins) + grp->assign_pin(pin); } carry4->add_boolean_function("CO(0)", BooleanFunction::from_string("((S(0) & (CI | CYINIT)) | ((! S(0)) & DI(0)))").get()); @@ -1510,4 +1526,4 @@ namespace hal return is_equal; } } // namespace test_utils -} // namespace hal \ No newline at end of file +} // namespace hal From cfb43bc214a955d96c9a609d973e31aecad8985c Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 4 Oct 2023 19:00:30 +0200 Subject: [PATCH 22/66] Pin- and pingroup automatted tests should be fixed by now --- .../src/gate_library_test_utils.cpp | 299 +++++++++--------- 1 file changed, 147 insertions(+), 152 deletions(-) diff --git a/tests/test_utils/src/gate_library_test_utils.cpp b/tests/test_utils/src/gate_library_test_utils.cpp index 83d94cb8edd..c074221da03 100644 --- a/tests/test_utils/src/gate_library_test_utils.cpp +++ b/tests/test_utils/src/gate_library_test_utils.cpp @@ -12,6 +12,21 @@ namespace hal { namespace test_utils { + + void dumpPingroups(const GateType *gt) + { + std::cerr << "gate type: " << gt->get_id() << " <" << gt->get_name() << ">\n"; + for (PinGroup* pg : gt->get_pin_groups()) + { + std::cerr << " grp: " << pg->get_id() << (pg->is_ascending()?" asc ": " des ") << pg->get_start_index() + << " <" << pg->get_name() << ">\n"; + for (GatePin* pin : pg->get_pins()) + std::cerr << " pin: " << pin->get_id() << " inx:" << pin->get_group().second << " <" << pin->get_name() << ">\n"; + } + std::cerr << "-------------" << std::endl; + } + + std::unique_ptr create_gate_library(const std::filesystem::path& file_path) { std::unique_ptr lib = std::unique_ptr(new GateLibrary(file_path, "TESTING_GATE_LIBRARY")); @@ -416,201 +431,179 @@ namespace hal return nullptr; } - if (auto res_grp = carry4->create_pin_group("DI", {}, PinDirection::input, PinType::none, false, 3); res_grp.is_error()) + std::vector temp_pins; + + //--- pingroup DI --- + if (auto res = carry4->create_pin("DI(3)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - const auto grp = res_grp.get(); - - std::list pins; - if (auto res = carry4->create_pin("DI(0)", PinDirection::input, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("DI(1)", PinDirection::input, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("DI(2)", PinDirection::input, PinType::none, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("DI(2)", PinDirection::input, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("DI(1)", PinDirection::input, PinType::none, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("DI(3)", PinDirection::input, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("DI(0)", PinDirection::input, PinType::none, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - for (auto pin : pins) - grp->assign_pin(pin); + if (auto res_grp = carry4->create_pin_group("DI", temp_pins, PinDirection::input, PinType::none, false, 3); res_grp.is_error()) + { + return nullptr; } + temp_pins.clear(); - if (auto res_grp = carry4->create_pin_group("S", {}, PinDirection::input, PinType::none, false, 3); res_grp.is_error()) + //--- pingroup S ---- + if (auto res = carry4->create_pin("S(3)", PinDirection::input, PinType::none, false); res.is_error()) { return nullptr; } else { - const auto grp = res_grp.get(); - - std::list pins; - if (auto res = carry4->create_pin("S(0)", PinDirection::input, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("S(1)", PinDirection::input, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("S(2)", PinDirection::input, PinType::none, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("S(2)", PinDirection::input, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("S(1)", PinDirection::input, PinType::none, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("S(3)", PinDirection::input, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("S(0)", PinDirection::input, PinType::none, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - for (auto pin : pins) - grp->assign_pin(pin); + if (auto res_grp = carry4->create_pin_group("S", temp_pins, PinDirection::input, PinType::none, false, 3); res_grp.is_error()) + { + return nullptr; } + temp_pins.clear(); - if (auto res_grp = carry4->create_pin_group("CO", {}, PinDirection::output, PinType::carry, false, 3); res_grp.is_error()) + //--- pingroup CO --- + if (auto res = carry4->create_pin("CO(3)", PinDirection::output, PinType::carry, false); res.is_error()) { return nullptr; } else { - const auto grp = res_grp.get(); - - std::list pins; - if (auto res = carry4->create_pin("CO(0)", PinDirection::output, PinType::carry, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("CO(1)", PinDirection::output, PinType::carry, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("CO(2)", PinDirection::output, PinType::carry, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("CO(2)", PinDirection::output, PinType::carry, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("CO(1)", PinDirection::output, PinType::carry, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("CO(3)", PinDirection::output, PinType::carry, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("CO(0)", PinDirection::output, PinType::carry, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - for (auto pin : pins) - grp->assign_pin(pin); + if (auto res_grp = carry4->create_pin_group("CO", temp_pins, PinDirection::output, PinType::carry, false, 3); res_grp.is_error()) + { + return nullptr; } + temp_pins.clear(); - if (auto res_grp = carry4->create_pin_group("O", {}, PinDirection::output, PinType::none, false, 3); res_grp.is_error()) + //--- pingroup O ---- + if (auto res = carry4->create_pin("O(3)", PinDirection::output, PinType::none, false); res.is_error()) { return nullptr; } else { - const auto grp = res_grp.get(); - - std::list pins; - if (auto res = carry4->create_pin("O(0)", PinDirection::output, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("O(1)", PinDirection::output, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("O(2)", PinDirection::output, PinType::none, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("O(2)", PinDirection::output, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("O(1)", PinDirection::output, PinType::none, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - if (auto res = carry4->create_pin("O(3)", PinDirection::output, PinType::none, false); res.is_error()) - { - return nullptr; - } - else - { - pins.push_front(res.get()); - } + if (auto res = carry4->create_pin("O(0)", PinDirection::output, PinType::none, false); res.is_error()) + { + return nullptr; + } + else + { + temp_pins.push_back(res.get()); + } - for (auto pin : pins) - grp->assign_pin(pin); + if (auto res_grp = carry4->create_pin_group("O", temp_pins, PinDirection::output, PinType::none, false, 3); res_grp.is_error()) + { + return nullptr; } + temp_pins.clear(); carry4->add_boolean_function("CO(0)", BooleanFunction::from_string("((S(0) & (CI | CYINIT)) | ((! S(0)) & DI(0)))").get()); carry4->add_boolean_function("CO(1)", BooleanFunction::from_string("((S(1) & CO(0)) | ((! S(1)) & DI(1)))").get()); @@ -1389,6 +1382,8 @@ namespace hal if (!gate_pin_groups_are_equal(*pg1_it, *pg2_it)) { log_info("test_utils", "unequal pin groups of gate types with names '{}' and '{}'", gt1->get_name(), gt2->get_name()); + test_utils::dumpPingroups(gt1); + test_utils::dumpPingroups(gt2); return false; } } From 947a6dd2f5591781f87960f34ba719957365648a Mon Sep 17 00:00:00 2001 From: joern274 Date: Thu, 5 Oct 2023 17:12:40 +0200 Subject: [PATCH 23/66] Modification in pin test reverted --- tests/netlist/module.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/netlist/module.cpp b/tests/netlist/module.cpp index 5e4dc48c573..1c85e69f817 100644 --- a/tests/netlist/module.cpp +++ b/tests/netlist/module.cpp @@ -1151,7 +1151,7 @@ namespace hal { EXPECT_EQ(in_group->get_pin_at_index(1).get(), in_pin_0); EXPECT_EQ(in_group->get_pin_at_index(2).get(), in_pin_1); - EXPECT_TRUE(m_1->move_pin_within_group(in_group, in_pin_0, 2)); + EXPECT_TRUE(m_1->move_pin_within_group(in_group, in_pin_1, 1)); // descending group start index 2 // in_pin_0 "I0" index 2 // in_pin_1 "I1" index 1 From 5d04758a0c4ff7fc42c6f76dd09661d7df607ed4 Mon Sep 17 00:00:00 2001 From: SJulianS <18482153+SJulianS@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:32:23 +0100 Subject: [PATCH 24/66] Update README.md (#541) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 428f156f796..536a7b20a78 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ This repository contains a selection of curated plugins: A comprehensive documentation of HAL's features from a user perspective is available in our [Wiki](https://github.com/emsec/hal/wiki). In addition, we provide a full [C++ API](https://emsec.github.io/hal/doc/) and [Python API](https://emsec.github.io/hal/pydoc/) documentation. ## Slack, Contact and Support -For all kinds of inquiries, please contact us using our dedicated e-mail address: [hal@csp.mpg.de](mailto:hal@csp.mpg.de). To receive an invite to our dedicated hal-support Slack workspace, please write us an e-mail as well. +For all kinds of inquiries, please contact us using our dedicated e-mail address: [hal@mpi-sp.org](mailto:hal@mpi-sp.org). # Build Instructions From 8a1a1034c841e5767a4a4c8da37466015c079016 Mon Sep 17 00:00:00 2001 From: joern274 Date: Tue, 28 Nov 2023 21:09:06 +0100 Subject: [PATCH 25/66] bugfix: broken GUI API select routines --- plugins/gui/src/gui_api/gui_api.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/gui/src/gui_api/gui_api.cpp b/plugins/gui/src/gui_api/gui_api.cpp index 07734133b79..83f8180bfba 100644 --- a/plugins/gui/src/gui_api/gui_api.cpp +++ b/plugins/gui/src/gui_api/gui_api.cpp @@ -87,7 +87,7 @@ namespace hal gSelectionRelay->addGate(gate->get_id()); gSelectionRelay->setFocus(SelectionRelay::ItemType::Gate,gate->get_id()); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); if(navigate_to_selection) Q_EMIT navigationRequested(); @@ -115,7 +115,7 @@ namespace hal gate_ids.unite(gSelectionRelay->selectedGates()); gSelectionRelay->setSelectedGates(gate_ids); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); if(navigate_to_selection) Q_EMIT navigationRequested(); @@ -138,7 +138,7 @@ namespace hal gSelectionRelay->addNet(net->get_id()); gSelectionRelay->setFocus(SelectionRelay::ItemType::Net,net->get_id()); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); if(navigate_to_selection) Q_EMIT navigationRequested(); @@ -166,7 +166,7 @@ namespace hal net_ids.unite(gSelectionRelay->selectedNets()); gSelectionRelay->setSelectedNets(net_ids); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); if(navigate_to_selection) Q_EMIT navigationRequested(); @@ -189,7 +189,7 @@ namespace hal gSelectionRelay->addModule(module->get_id()); gSelectionRelay->setFocus(SelectionRelay::ItemType::Module,module->get_id()); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); if(navigate_to_selection) Q_EMIT navigationRequested(); @@ -217,7 +217,7 @@ namespace hal module_ids.unite(gSelectionRelay->selectedModules()); gSelectionRelay->setSelectedModules(module_ids); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); if(navigate_to_selection) Q_EMIT navigationRequested(); @@ -290,7 +290,7 @@ namespace hal return; gSelectionRelay->removeGate(gate->get_id()); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); } void GuiApi::deselectGate(u32 gate_id) @@ -312,7 +312,7 @@ namespace hal } gSelectionRelay->setSelectedGates(gate_ids); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); } void GuiApi::deselectGate(const std::vector& gate_ids) @@ -328,7 +328,7 @@ namespace hal return; gSelectionRelay->removeNet(net->get_id()); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); } void GuiApi::deselectNet(u32 netId) @@ -350,7 +350,7 @@ namespace hal } gSelectionRelay->setSelectedNets(net_ids); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); } void GuiApi::deselectNet(const std::vector& net_ids) @@ -366,7 +366,7 @@ namespace hal return; gSelectionRelay->removeModule(module->get_id()); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); } void GuiApi::deselectModule(u32 module_id) @@ -387,7 +387,7 @@ namespace hal } gSelectionRelay->setSelectedModules(module_ids); - gSelectionRelay->selectionChanged(this); + gSelectionRelay->relaySelectionChanged(this); } void GuiApi::deselectModule(const std::vector& module_ids) From b1c44eb5e21d0e64a4f73aab6c05c6f6b34f817a Mon Sep 17 00:00:00 2001 From: nil1603 Date: Wed, 29 Nov 2023 10:09:31 +0100 Subject: [PATCH 26/66] added missing return --- .../module_details_widget/netlist_elements_tree_model.cpp | 1 + .../module_details_widget/port_tree_model.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/netlist_elements_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/netlist_elements_tree_model.cpp index 0073b6e8a0c..c17b91f3735 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/netlist_elements_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/netlist_elements_tree_model.cpp @@ -31,6 +31,7 @@ namespace hal return qvType; break;} } + return QVariant(); } void NetlistElementsTreeitem::setData(QList data) diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index ea11e74c5a5..c93c70820bf 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -44,6 +44,7 @@ namespace hal return qvNetName; break;} } + return QVariant(); } void PortTreeItem::setData(QList data) From 14bbba4d69e36682a4bf73c13892fe91025faf4c Mon Sep 17 00:00:00 2001 From: nil1603 Date: Wed, 29 Nov 2023 10:12:27 +0100 Subject: [PATCH 27/66] added missing return files --- .../gate_details_widget/pin_tree_model.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/gui/src/selection_details_widget/gate_details_widget/pin_tree_model.cpp b/plugins/gui/src/selection_details_widget/gate_details_widget/pin_tree_model.cpp index 3b2a9e9138b..c4b62870444 100644 --- a/plugins/gui/src/selection_details_widget/gate_details_widget/pin_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/gate_details_widget/pin_tree_model.cpp @@ -40,6 +40,7 @@ namespace hal return qvNetName; break;} } + return QVariant(); } void PinTreeItem::setData(QList data) From f7c4ef2cbc862d0db763ee1930f2192ea8378435 Mon Sep 17 00:00:00 2001 From: nil1603 Date: Wed, 29 Nov 2023 10:18:02 +0100 Subject: [PATCH 28/66] added missing return --- .../module_details_widget/module_tree_model.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_tree_model.cpp index c6b16cdd36f..76b19c5a5f8 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_tree_model.cpp @@ -30,6 +30,7 @@ namespace hal return qvType; break;} } + return QVariant(); } void ModuleTreeitem::setData(QList data) From 23be4d82d2df78bfd2bf5b499333da71cde1a1fe Mon Sep 17 00:00:00 2001 From: MonsterDruide1 <5958456@gmail.com> Date: Wed, 29 Nov 2023 15:29:05 +0100 Subject: [PATCH 29/66] gui/graph_widget: Allow pan with middle mouse button --- .../src/graph_widget/graph_graphics_view.cpp | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/plugins/gui/src/graph_widget/graph_graphics_view.cpp b/plugins/gui/src/graph_widget/graph_graphics_view.cpp index f366d96afea..ea827bd4d11 100644 --- a/plugins/gui/src/graph_widget/graph_graphics_view.cpp +++ b/plugins/gui/src/graph_widget/graph_graphics_view.cpp @@ -381,11 +381,8 @@ namespace hal if(dynamic_cast(itemAt(event->pos()))) return; - if (event->modifiers() == mPanModifier) - { - if (event->button() == Qt::LeftButton) - mMovePosition = event->pos(); - } + if ((event->modifiers() == mPanModifier && event->button() == Qt::LeftButton) || event->button() == Qt::MidButton) + mMovePosition = event->pos(); else if (event->button() == Qt::LeftButton) { GraphicsItem* item = static_cast(itemAt(event->pos())); @@ -435,32 +432,29 @@ namespace hal mTargetScenePos = mapToScene(event->pos()); } - if (event->buttons().testFlag(Qt::LeftButton)) + if ((event->buttons().testFlag(Qt::LeftButton) && event->modifiers() == mPanModifier) || event->buttons().testFlag(Qt::MidButton)) { - if (event->modifiers() == mPanModifier) - { - QScrollBar* hBar = horizontalScrollBar(); - QScrollBar* vBar = verticalScrollBar(); - QPoint delta_move = event->pos() - mMovePosition; - mMovePosition = event->pos(); - hBar->setValue(hBar->value() + (isRightToLeft() ? delta_move.x() : -delta_move.x())); - vBar->setValue(vBar->value() - delta_move.y()); - } - else + QScrollBar* hBar = horizontalScrollBar(); + QScrollBar* vBar = verticalScrollBar(); + QPoint delta_move = event->pos() - mMovePosition; + mMovePosition = event->pos(); + hBar->setValue(hBar->value() + (isRightToLeft() ? delta_move.x() : -delta_move.x())); + vBar->setValue(vBar->value() - delta_move.y()); + } + else if (event->buttons().testFlag(Qt::LeftButton)) + { + if (mDragItem && (event->pos() - mDragMousedownPosition).manhattanLength() >= QApplication::startDragDistance()) { - if (mDragItem && (event->pos() - mDragMousedownPosition).manhattanLength() >= QApplication::startDragDistance()) - { - QDrag* drag = new QDrag(this); - QMimeData* mimeData = new QMimeData; + QDrag* drag = new QDrag(this); + QMimeData* mimeData = new QMimeData; - // TODO set MIME type and icon - mimeData->setText("dragTest"); - drag->setMimeData(mimeData); - // drag->setPixmap(iconPixmap); + // TODO set MIME type and icon + mimeData->setText("dragTest"); + drag->setMimeData(mimeData); + // drag->setPixmap(iconPixmap); - // enable DragMoveEvents until mouse released - drag->exec(Qt::MoveAction); - } + // enable DragMoveEvents until mouse released + drag->exec(Qt::MoveAction); } } #ifdef GUI_DEBUG_GRID From 97ec997e0379a5fb3b3c078a0a0ec2ece8658d18 Mon Sep 17 00:00:00 2001 From: joern274 Date: Thu, 30 Nov 2023 21:23:54 +0100 Subject: [PATCH 30/66] Added user setting whether middle mouse button will pan --- .../include/gui/graph_widget/graph_context_manager.h | 2 ++ plugins/gui/src/graph_widget/graph_context_manager.cpp | 7 +++++++ plugins/gui/src/graph_widget/graph_graphics_view.cpp | 10 ++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/plugins/gui/include/gui/graph_widget/graph_context_manager.h b/plugins/gui/include/gui/graph_widget/graph_context_manager.h index 494e14bddfc..357ab323de0 100644 --- a/plugins/gui/include/gui/graph_widget/graph_context_manager.h +++ b/plugins/gui/include/gui/graph_widget/graph_context_manager.h @@ -364,6 +364,8 @@ namespace hal } static SettingsItemCheckbox* sSettingNetGroupingToPins; + + static SettingsItemCheckbox* sSettingPanOnMiddleButton; Q_SIGNALS: /** * Q_SIGNAL that notifies about the creation of a new context by the context manager. diff --git a/plugins/gui/src/graph_widget/graph_context_manager.cpp b/plugins/gui/src/graph_widget/graph_context_manager.cpp index a0be39acec9..9f1b6dc4c55 100644 --- a/plugins/gui/src/graph_widget/graph_context_manager.cpp +++ b/plugins/gui/src/graph_widget/graph_context_manager.cpp @@ -30,6 +30,13 @@ namespace hal "Appearance:Graph View", "If set net grouping colors are also applied to input and output pins of gates"); + SettingsItemCheckbox* GraphContextManager::sSettingPanOnMiddleButton = new SettingsItemCheckbox("Pan with Middle Mouse Button", + "graph_view/pan_middle_button", + false, + "Graph View", + "If enabled middle mouse button will pan the graphics.\n" + "If disabled middle mouse button can be used for rubber band selection."); + GraphContextManager::GraphContextManager() : mContextTableModel(new ContextTableModel()), mMaxContextId(0) { mSettingDebugGrid = new SettingsItemCheckbox("GUI Debug Grid", diff --git a/plugins/gui/src/graph_widget/graph_graphics_view.cpp b/plugins/gui/src/graph_widget/graph_graphics_view.cpp index ea827bd4d11..95d7d28d940 100644 --- a/plugins/gui/src/graph_widget/graph_graphics_view.cpp +++ b/plugins/gui/src/graph_widget/graph_graphics_view.cpp @@ -15,6 +15,7 @@ #include "gui/content_manager/content_manager.h" #include "gui/context_manager_widget/context_manager_widget.h" #include "gui/graph_tab_widget/graph_tab_widget.h" +#include "gui/graph_widget/graph_context_manager.h" #include "gui/grouping/grouping_manager_widget.h" #include "gui/grouping/grouping_table_model.h" #include "gui/grouping_dialog/grouping_dialog.h" @@ -38,6 +39,7 @@ #include "gui/module_dialog/module_dialog.h" #include "gui/module_dialog/gate_dialog.h" #include "gui/comment_system/widgets/comment_dialog.h" +#include "gui/settings/settings_items/settings_item_checkbox.h" #include "hal_core/netlist/gate.h" #include "hal_core/netlist/grouping.h" #include "hal_core/netlist/module.h" @@ -381,8 +383,11 @@ namespace hal if(dynamic_cast(itemAt(event->pos()))) return; - if ((event->modifiers() == mPanModifier && event->button() == Qt::LeftButton) || event->button() == Qt::MidButton) + if ((event->modifiers() == mPanModifier && event->button() == Qt::LeftButton) || + (event->button() == Qt::MidButton && gGraphContextManager->sSettingPanOnMiddleButton->value().toBool())) + { mMovePosition = event->pos(); + } else if (event->button() == Qt::LeftButton) { GraphicsItem* item = static_cast(itemAt(event->pos())); @@ -432,7 +437,8 @@ namespace hal mTargetScenePos = mapToScene(event->pos()); } - if ((event->buttons().testFlag(Qt::LeftButton) && event->modifiers() == mPanModifier) || event->buttons().testFlag(Qt::MidButton)) + if ((event->buttons().testFlag(Qt::LeftButton) && event->modifiers() == mPanModifier) || + (event->buttons().testFlag(Qt::MidButton) && gGraphContextManager->sSettingPanOnMiddleButton->value().toBool())) { QScrollBar* hBar = horizontalScrollBar(); QScrollBar* vBar = verticalScrollBar(); From 160b01d0cb3f4e5e1182c614994206bf76da5bfb Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 4 Dec 2023 21:48:14 +0100 Subject: [PATCH 31/66] Change drag 'shadow' to list of rects --- .../items/utility_items/node_drag_shadow.h | 7 +++- .../items/utility_items/node_drag_shadow.cpp | 42 ++++++++++++------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h b/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h index 5b773285853..e5de6244192 100644 --- a/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h +++ b/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h @@ -26,6 +26,8 @@ #pragma once #include +#include +#include namespace hal { @@ -50,12 +52,14 @@ namespace hal void start(const QPointF& posF, const QSizeF& sizeF); void stop(); + /* qreal width() const; qreal height() const; QSizeF size() const; void setWidth(const qreal width); void setHeight(const qreal height); +*/ void setVisualCue(const DragCue cue); @@ -76,7 +80,6 @@ namespace hal DragCue mCue; - qreal mWidth; - qreal mHeight; + QList mNodeShapes; }; } // namespace hal diff --git a/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp b/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp index 60f746f9d47..344d6c5ad7d 100644 --- a/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp +++ b/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp @@ -41,15 +41,14 @@ namespace hal hide(); setAcceptedMouseButtons(0); - mWidth = 100; - mHeight = 100; + mNodeShapes.append(QRectF(0,0,100,100)); } void NodeDragShadow::start(const QPointF& posF, const QSizeF& sizeF) { setPos(posF); - setWidth(sizeF.width()); - setHeight(sizeF.height()); + mNodeShapes.clear(); + mNodeShapes.append(QRectF(QPointF(0,0),sizeF)); setZValue(1); show(); } @@ -59,6 +58,7 @@ namespace hal hide(); } + /* qreal NodeDragShadow::width() const { return mWidth; @@ -83,6 +83,7 @@ namespace hal { mHeight = height; } +*/ void NodeDragShadow::setLod(const qreal lod) { @@ -106,27 +107,40 @@ namespace hal sPen.setColor(sColorPen[color_index]); painter->setPen(sPen); - if (sLod < 0.5) + for (const QRectF& rect : mNodeShapes) { - painter->fillRect(QRectF(0, 0, mWidth, mHeight), sColorSolid[color_index]); - } - else - { - QRectF rect = QRectF(0, 0, mWidth, mHeight); - painter->drawRect(rect); - painter->fillRect(rect, sColorTranslucent[color_index]); + if (sLod < 0.5) + { + + painter->fillRect(rect, sColorSolid[color_index]); + } + else + { + painter->drawRect(rect); + painter->fillRect(rect, sColorTranslucent[color_index]); + } } } QRectF NodeDragShadow::boundingRect() const { - return QRectF(0, 0, mWidth, mHeight); + QRectF retval; + if (mNodeShapes.isEmpty()) return retval; + for (const QRectF& rect : mNodeShapes) + { + if (retval.isNull()) + retval = rect; + else + retval = retval.united(rect); + } + return retval; } QPainterPath NodeDragShadow::shape() const { QPainterPath path; - path.addRect(QRectF(0, 0, mWidth, mHeight)); + for (const QRectF& rect : mNodeShapes) + path.addRect(rect); return path; } } From b2a35b1e804ee006137de4cfd30e4dac6f677171 Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 4 Dec 2023 22:03:00 +0100 Subject: [PATCH 32/66] Crash fix : check whether junction routing was done missing --- plugins/gui/src/graph_widget/layouters/graph_layouter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp b/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp index dae127f6260..9071800273f 100644 --- a/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp +++ b/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp @@ -1362,14 +1362,14 @@ namespace hal // netjunction -> endpoint auto itEpc = mLayouter->mEndpointHash.find(wToPoint); y0 = mLayouter->mCoordArrayY->lanePosition(iy0, j0? j0->rect().bottom() : 0); - y1 = itEpc != mLayouter->mEndpointHash.constEnd() ? itEpc.value().lanePosition(j1->rect().top(), true) + y1 = itEpc != mLayouter->mEndpointHash.constEnd() ? itEpc.value().lanePosition(j1 ? j1->rect().top() : 0, true) : mLayouter->mCoordArrayY->lanePosition(iy1,0); } else { // endpoint -> netjunction auto itEpc = mLayouter->mEndpointHash.find(wFromPoint); - y0 = itEpc != mLayouter->mEndpointHash.constEnd() ? itEpc.value().lanePosition(j0->rect().bottom(), true) + y0 = itEpc != mLayouter->mEndpointHash.constEnd() ? itEpc.value().lanePosition(j0 ? j0->rect().bottom() : 0, true) : mLayouter->mCoordArrayY->lanePosition(iy0,0); y1 = mLayouter->mCoordArrayY->lanePosition(iy1, j1? j1->rect().top() : 0); } From ceb241d08afe2d6093623a77fe580efa4da02bb1 Mon Sep 17 00:00:00 2001 From: Skaleee <78816681+Skaleee@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:04:19 +0100 Subject: [PATCH 33/66] Open Gate in View on Doubleclick --- .../gui/include/gui/graph_widget/graph_context_manager.h | 2 +- plugins/gui/src/module_widget/module_widget.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/gui/include/gui/graph_widget/graph_context_manager.h b/plugins/gui/include/gui/graph_widget/graph_context_manager.h index 357ab323de0..9e6bec315c1 100644 --- a/plugins/gui/include/gui/graph_widget/graph_context_manager.h +++ b/plugins/gui/include/gui/graph_widget/graph_context_manager.h @@ -116,7 +116,7 @@ namespace hal bool contextWithNameExists(const QString& name) const; /** - * Generate next view with given prefix + * Generate next view name with given prefix * @param prefix * @return the view name which does not exist so far */ diff --git a/plugins/gui/src/module_widget/module_widget.cpp b/plugins/gui/src/module_widget/module_widget.cpp index b10a12eb9be..772ef910ec4 100644 --- a/plugins/gui/src/module_widget/module_widget.cpp +++ b/plugins/gui/src/module_widget/module_widget.cpp @@ -474,7 +474,13 @@ namespace hal void ModuleWidget::handleItemDoubleClicked(const QModelIndex& index) { - openModuleInView(index); + ModuleItem* mi = getModuleItemFromIndex(index); + switch(mi->getType()){ + case ModuleItem::TreeItemType::Module: openModuleInView(index); break; + case ModuleItem::TreeItemType::Gate: openGateInView(index); break; + case ModuleItem::TreeItemType::Net: //openNetEndpointsInView(index); + break; + } } void ModuleWidget::openModuleInView(const QModelIndex& index) From 27ffe3557d81025e0a567a4133a43dc66377842b Mon Sep 17 00:00:00 2001 From: Skaleee <78816681+Skaleee@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:00:00 +0100 Subject: [PATCH 34/66] open view on doubleclick on net with all connected gates --- .../include/gui/module_widget/module_widget.h | 2 ++ .../gui/src/module_widget/module_widget.cpp | 22 +++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/plugins/gui/include/gui/module_widget/module_widget.h b/plugins/gui/include/gui/module_widget/module_widget.h index d3df33a65b2..fb18026a2c7 100644 --- a/plugins/gui/include/gui/module_widget/module_widget.h +++ b/plugins/gui/include/gui/module_widget/module_widget.h @@ -276,6 +276,8 @@ namespace hal void openGateInView(const QModelIndex& index); + void openNetEndpointsInView(const QModelIndex &index); + void changeGateName(const QModelIndex& index); void changeNetName(const QModelIndex& index); diff --git a/plugins/gui/src/module_widget/module_widget.cpp b/plugins/gui/src/module_widget/module_widget.cpp index 772ef910ec4..6ecafe135df 100644 --- a/plugins/gui/src/module_widget/module_widget.cpp +++ b/plugins/gui/src/module_widget/module_widget.cpp @@ -478,8 +478,7 @@ namespace hal switch(mi->getType()){ case ModuleItem::TreeItemType::Module: openModuleInView(index); break; case ModuleItem::TreeItemType::Gate: openGateInView(index); break; - case ModuleItem::TreeItemType::Net: //openNetEndpointsInView(index); - break; + case ModuleItem::TreeItemType::Net: openNetEndpointsInView(index); break; } } @@ -504,6 +503,25 @@ namespace hal act->exec(); } + void ModuleWidget::openNetEndpointsInView(const QModelIndex &index){ + QSet gates; + + Net* net = gNetlist->get_net_by_id(getModuleItemFromIndex(index)->id()); + for(auto endpoint : net->get_sources()) + gates.insert(endpoint->get_gate()->get_id()); + for(auto endpoint : net->get_destinations()) + gates.insert(endpoint->get_gate()->get_id()); + + QString name = gGraphContextManager->nextViewName("Isolated View"); + + UserActionCompound* act = new UserActionCompound; + act->setUseCreatedObject(); + act->addAction(new ActionCreateObject(UserActionObjectType::Context, name)); + act->addAction(new ActionAddItemsToObject({}, gates)); + //Add placement hints + act->exec(); + } + void ModuleWidget::changeGateName(const QModelIndex &index) { QString oldName = getModuleItemFromIndex(index)->name(); From ac407fc94fb5f6dd711bbe96b2df374cc9023c66 Mon Sep 17 00:00:00 2001 From: Skaleee <78816681+Skaleee@users.noreply.github.com> Date: Wed, 6 Dec 2023 23:10:11 +0100 Subject: [PATCH 35/66] ordered sources and destinations on a grid --- .../gui/src/module_widget/module_widget.cpp | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/plugins/gui/src/module_widget/module_widget.cpp b/plugins/gui/src/module_widget/module_widget.cpp index 6ecafe135df..be8d6416189 100644 --- a/plugins/gui/src/module_widget/module_widget.cpp +++ b/plugins/gui/src/module_widget/module_widget.cpp @@ -504,21 +504,32 @@ namespace hal } void ModuleWidget::openNetEndpointsInView(const QModelIndex &index){ - QSet gates; + QSet allGates; Net* net = gNetlist->get_net_by_id(getModuleItemFromIndex(index)->id()); - for(auto endpoint : net->get_sources()) - gates.insert(endpoint->get_gate()->get_id()); - for(auto endpoint : net->get_destinations()) - gates.insert(endpoint->get_gate()->get_id()); + + PlacementHint plc(PlacementHint::PlacementModeType::GridPosition); + int currentY = -(int)(net->get_num_of_sources()/2); + for(auto endpoint : net->get_sources()) { + u32 id = endpoint->get_gate()->get_id(); + allGates.insert(id); + plc.addGridPosition(Node(id, Node::NodeType::Gate), {0, currentY++}); + } + currentY = -(int)(net->get_num_of_destinations()/2); + for(auto endpoint : net->get_destinations()) { + u32 id = endpoint->get_gate()->get_id(); + allGates.insert(id); + plc.addGridPosition(Node(id, Node::NodeType::Gate), {1, currentY++}); + } QString name = gGraphContextManager->nextViewName("Isolated View"); - + UserActionCompound* act = new UserActionCompound; act->setUseCreatedObject(); act->addAction(new ActionCreateObject(UserActionObjectType::Context, name)); - act->addAction(new ActionAddItemsToObject({}, gates)); - //Add placement hints + auto actionAITO = new ActionAddItemsToObject({}, allGates); + actionAITO->setPlacementHint(plc); + act->addAction(actionAITO); act->exec(); } From ea87933a0f3bcfa6753f386adeb1b5660689add6 Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 11 Dec 2023 15:30:32 +0100 Subject: [PATCH 36/66] New DragController class shall handle logical drag as well as vizualisation --- .../gui/graph_widget/drag_controller.h | 82 ++++++ .../gui/graph_widget/graph_graphics_view.h | 9 +- .../include/gui/graph_widget/graphics_scene.h | 24 -- .../items/utility_items/node_drag_shadow.h | 4 +- .../graph_widget/layouters/graph_layouter.h | 3 + .../gui/src/graph_widget/drag_controller.cpp | 237 ++++++++++++++++++ .../src/graph_widget/graph_graphics_view.cpp | 78 ++---- .../gui/src/graph_widget/graphics_scene.cpp | 30 +-- .../items/utility_items/node_drag_shadow.cpp | 47 +--- .../graph_widget/layouters/graph_layouter.cpp | 17 ++ 10 files changed, 374 insertions(+), 157 deletions(-) create mode 100644 plugins/gui/include/gui/graph_widget/drag_controller.h create mode 100644 plugins/gui/src/graph_widget/drag_controller.cpp diff --git a/plugins/gui/include/gui/graph_widget/drag_controller.h b/plugins/gui/include/gui/graph_widget/drag_controller.h new file mode 100644 index 00000000000..dad3077ed96 --- /dev/null +++ b/plugins/gui/include/gui/graph_widget/drag_controller.h @@ -0,0 +1,82 @@ +// MIT License +// +// Copyright (c) 2019 Ruhr University Bochum, Chair for Embedded Security. All Rights reserved. +// Copyright (c) 2019 Marc Fyrbiak, Sebastian Wallat, Max Hoffmann ("ORIGINAL AUTHORS"). All rights reserved. +// Copyright (c) 2021 Max Planck Institute for Security and Privacy. All Rights reserved. +// Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#include +#include +#include +#include +#include "gui/gui_def.h" +#include "gui/graph_widget/items/utility_items/node_drag_shadow.h" + +namespace hal { + + class GraphWidget; + class GraphicsNode; + class NodeBox; + class NodeDragShadow; + + class DragController : public QObject + { + Q_OBJECT + GraphWidget* mGraphWidget; + bool mDropAllowed; + bool mWantSwap; + QPoint mMousedownPosition; + NodeBox* mDragNodeBox; + QPoint mCurrentGridpos; + QSet mAdditionalBoxes; + QHash mShadows; + + void setSwapIntent(bool wantSwap); + void addShadow(const NodeBox* nb); + public: + DragController(GraphWidget* gw, QObject* parent = nullptr); + + void clear(); + + void set(GraphicsNode* drgItem, const QPoint& eventPos); + + /** + * Starts the dragging of a gate or module to show its shadow meanwhile. + * + * @param wantSwap - True if keyboard swap modifier has been pressed, false otherwise + */ + void enterDrag(bool wantSwap); + + /** + * Moves the shadow that appears while dragging a gate or module. + * + * @param eventPos - The mouse position in scene coordinates while dragging + * @param wantSwap - True if keyboard swap modifier has been pressed, false otherwise + * @param gridPos - The new grid position of the primary node + */ + void move(const QPoint& eventPos, bool wantSwap, const QPoint& gridPos); + bool hasDragged(const QPoint& eventPos); + int isDropAllowed() const; + QHash finalNodePositions() const; + NodeDragShadow::DragCue dragCue() const; + }; +} diff --git a/plugins/gui/include/gui/graph_widget/graph_graphics_view.h b/plugins/gui/include/gui/graph_widget/graph_graphics_view.h index 88519baf70f..2d3b5201bfd 100644 --- a/plugins/gui/include/gui/graph_widget/graph_graphics_view.h +++ b/plugins/gui/include/gui/graph_widget/graph_graphics_view.h @@ -40,6 +40,7 @@ namespace hal { class GraphicsItem; class GraphWidget; + class DragController; namespace graph_widget_constants { @@ -233,13 +234,7 @@ namespace hal bool mGridClustersEnabled; GraphicsScene::GridType mGridType; - QPoint mDragMousedownPosition; - QPoint mDragStartGridpos; - GraphicsGate* mDragItem; - QPoint mDragCurrentGridpos; - bool mDragCurrentModifier; - bool mDropAllowed; - + DragController* mDragController; Qt::KeyboardModifier mDragModifier; QPoint mMovePosition; diff --git a/plugins/gui/include/gui/graph_widget/graphics_scene.h b/plugins/gui/include/gui/graph_widget/graphics_scene.h index 08182287355..910f1f1e5d4 100644 --- a/plugins/gui/include/gui/graph_widget/graphics_scene.h +++ b/plugins/gui/include/gui/graph_widget/graphics_scene.h @@ -119,28 +119,6 @@ namespace hal */ ~GraphicsScene(); - /** - * Starts the dragging of a gate or module to show its shadow meanwhile. - * - * @param posF - The position of the shadow - * @param sizeF - The size of the shadow (i.e. the size of the dragged gate) - * @param cue - The cue of the current position - */ - void startDragShadow(const QPointF& posF, const QSizeF& sizeF, const NodeDragShadow::DragCue cue); - - /** - * Moves the shadow that appears while dragging a gate or module. - * - * @param posF - The new position of the shadow - * @param cue - The cue of the current position - */ - void moveDragShadow(const QPointF& posF, const NodeDragShadow::DragCue cue); - - /** - * Removes the shadow that appears while dragging a gate or module (at the end of the drag action). - */ - void stopDragShadow(); - /** * Gets the position of the drag shadow. * @@ -321,8 +299,6 @@ namespace hal void drawBackground(QPainter* painter, const QRectF& rect) override; - NodeDragShadow* mDragShadowGate; - QVector mModuleItems; QVector mGateItems; QVector mNetItems; diff --git a/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h b/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h index e5de6244192..8189ddc98b4 100644 --- a/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h +++ b/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h @@ -62,6 +62,7 @@ namespace hal */ void setVisualCue(const DragCue cue); + QList multiMoveGridPositions() const; static void setLod(const qreal lod); static void loadSettings(); @@ -79,7 +80,6 @@ namespace hal static QColor sColorTranslucent[]; DragCue mCue; - - QList mNodeShapes; + QRectF mRect; }; } // namespace hal diff --git a/plugins/gui/include/gui/graph_widget/layouters/graph_layouter.h b/plugins/gui/include/gui/graph_widget/layouters/graph_layouter.h index 9324227d717..367678f3b7d 100644 --- a/plugins/gui/include/gui/graph_widget/layouters/graph_layouter.h +++ b/plugins/gui/include/gui/graph_widget/layouters/graph_layouter.h @@ -271,7 +271,10 @@ namespace hal const QMap nodeToPositionMap() const; const QMap positionToNodeMap() const; + QHash nodeToPositionHash() const; + NetLayoutPoint positonForNode(const Node& nd) const; + Node nodeAtPosition(const QPoint& p) const; QPoint gridPointByItem(GraphicsNode* item) const; diff --git a/plugins/gui/src/graph_widget/drag_controller.cpp b/plugins/gui/src/graph_widget/drag_controller.cpp new file mode 100644 index 00000000000..e2cad6980e6 --- /dev/null +++ b/plugins/gui/src/graph_widget/drag_controller.cpp @@ -0,0 +1,237 @@ +#include "gui/graph_widget/drag_controller.h" +#include "gui/graph_widget/graph_widget.h" +#include "gui/graph_widget/contexts/graph_context.h" +#include "gui/graph_widget/graphics_scene.h" +#include +#include + +namespace hal { + DragController::DragController(GraphWidget* gw, QObject *parent) + : QObject(parent), mGraphWidget(gw), mDragNodeBox(nullptr) + {;} + + void DragController::clear() + { + mDragNodeBox = nullptr; + mAdditionalBoxes.clear(); + mDropAllowed = false; + mWantSwap = false; + GraphicsScene* sc = mGraphWidget->getContext()->getLayouter()->scene(); + for (NodeDragShadow* nds : mShadows.values()) + { + if (sc) sc->removeItem(nds); + delete nds; + } + mShadows.clear(); + } + + NodeDragShadow::DragCue DragController::dragCue() const + { + if (!mDropAllowed) + return NodeDragShadow::DragCue::Rejected; + if (mWantSwap) + return NodeDragShadow::DragCue::Swappable; + return NodeDragShadow::DragCue::Movable; + } + + void DragController::set(GraphicsNode *drgItem, const QPoint &eventPos) + { + clear(); + // TODO: swap modifier -> deselect all but current + + QList nodesToMove; + for (u32 mid : gSelectionRelay->selectedModules()) + nodesToMove.append(Node(mid,Node::Module)); + for (u32 gid : gSelectionRelay->selectedGates()) + nodesToMove.append(Node(gid,Node::Gate)); + + auto context = mGraphWidget->getContext(); + const GraphLayouter* layouter = context->getLayouter(); + if (!layouter->done()) return; + + mMousedownPosition = eventPos; + + for (const Node& nd : nodesToMove) + { + NodeBox* nb = layouter->boxes().boxForNode(nd); + if (!nb) continue; + if (nb->item() == drgItem) + mDragNodeBox = nb; + else + mAdditionalBoxes.insert(nb); + } + + if (!mAdditionalBoxes.isEmpty()) mWantSwap = false; + qDebug() << "drag set " << (mDragNodeBox ? mDragNodeBox->id() : -1) << mAdditionalBoxes.size(); + } + + void DragController::setSwapIntent(bool wantSwap) + { + if (wantSwap == mWantSwap) return; + if (wantSwap) mAdditionalBoxes.clear(); + mWantSwap = wantSwap; + } + + void DragController::addShadow(const NodeBox* nb) + { + NodeDragShadow* nds = new NodeDragShadow; + nds->setVisualCue(dragCue()); + nds->start(nb->item()->pos(), nb->item()->boundingRect().size()); + GraphicsScene* sc = mGraphWidget->getContext()->getLayouter()->scene(); + if (sc) sc->addItem(nds); + mShadows.insert(nb,nds); + } + + void DragController::enterDrag(bool wantSwap) + { + qDebug() << "drag enter" << (mDragNodeBox ? mDragNodeBox->id() : -1) << mAdditionalBoxes.size(); + if (!mDragNodeBox) return; + setSwapIntent(wantSwap); + mCurrentGridpos = mDragNodeBox->gridPosition(); + mDropAllowed = false; + addShadow(mDragNodeBox); + for (NodeBox* nb : mAdditionalBoxes) + addShadow(nb); + } + + void DragController::move(const QPoint& eventPos, bool wantSwap, const QPoint& gridPos) + { + Q_UNUSED(eventPos); + if (!mDragNodeBox || (wantSwap == mWantSwap && gridPos == mCurrentGridpos)) return; + + setSwapIntent(wantSwap); + qDebug() << "drag set" << (mDragNodeBox ? mDragNodeBox->id() : -1) << mAdditionalBoxes.size(); + mCurrentGridpos = gridPos; + mDropAllowed = (isDropAllowed()==0); + + QPoint delta = mCurrentGridpos - mDragNodeBox->gridPosition(); + for (auto it = mShadows.constBegin(); it != mShadows.constEnd(); ++it) + { + it.value()->setVisualCue(dragCue()); + QPoint p = it.key()->gridPosition() + delta; + float x = mGraphWidget->getContext()->getLayouter()->gridXposition(p.x()); + float y = mGraphWidget->getContext()->getLayouter()->gridYposition(p.y()); + it.value()->setPos(QPointF(x,y)); + } + } + + bool DragController::hasDragged(const QPoint &eventPos) + { + if (!mDragNodeBox) return false; + return (eventPos - mMousedownPosition).manhattanLength() >= QApplication::startDragDistance(); + } + + int DragController::isDropAllowed() const + { + if (!mDragNodeBox) return -1; + if (mDragNodeBox->gridPosition() == mCurrentGridpos) return -2; + const NodeBoxes& boxes = mGraphWidget->getContext()->getLayouter()->boxes(); + + if (mWantSwap) + { + if (boxes.boxForPoint(mCurrentGridpos) != nullptr) + return 0; + return -3; + } + + QList pointsToCheck; + QSet freedPositions; + pointsToCheck.append(mCurrentGridpos); + freedPositions.insert(mDragNodeBox->gridPosition()); + + QPoint delta = mCurrentGridpos - mDragNodeBox->gridPosition(); + for (const NodeBox* nb : mAdditionalBoxes) + { + pointsToCheck.append(nb->gridPosition() + delta); + freedPositions.insert(nb->gridPosition()); + } + + for (const QPoint& p : pointsToCheck) + { + if (freedPositions.contains(p)) continue; + if (boxes.boxForPoint(p) != nullptr) return -4; + } + return 0; + } + /* + + auto context = mGraphWidget->getContext(); + const GraphLayouter* layouter = context->getLayouter(); + assert(layouter->done()); // ensure grid stable + + QMap::const_iterator node_iter = layouter->positionToNodeMap().find(mDragCurrentGridpos); + + NodeDragShadow::DragCue cue = NodeDragShadow::DragCue::Rejected; + // disallow dropping an item on itself + if (mDragCurrentGridpos != mDragStartGridpos) + { + if (swapModifier) + { + if (node_iter != layouter->positionToNodeMap().end()) + { + // allow move only on empty cells + cue = NodeDragShadow::DragCue::Swappable; + } + } + else + { + if (mDragAdditionalGridpos.isEmpty()) + { + // move single node + if (node_iter == layouter->positionToNodeMap().end()) + { + // allow move only on empty cells + cue = NodeDragShadow::DragCue::Movable; + } + } + else + { + // move multi nodes + if (node_iter == layouter->positionToNodeMap().end() || mDragAdditionalGridpos.contains(mDragCurrentGridpos) ) + { + cue = NodeDragShadow::DragCue::Movable; + for (QPoint p : mDragAdditionalGridpos) + { + p += mDragCurrentGridpos - mDragStartGridpos; + if (p != mDragStartGridpos && !mDragAdditionalGridpos.contains(p)) + { + if (layouter->positionToNodeMap().find(p) != layouter->positionToNodeMap().end()) + { + cue = NodeDragShadow::DragCue::Rejected; + break; + } + } + } + } + + } + } + } + mDropAllowed = (cue != NodeDragShadow::DragCue::Rejected); + + static_cast(scene())->moveDragShadow(snap.second, cue); + */ + + + QHash DragController::finalNodePositions() const + { + QHash retval = mGraphWidget->getContext()->getLayouter()->nodeToPositionHash(); + retval[mDragNodeBox->getNode()] = mCurrentGridpos; + if (mWantSwap) + { + Node targetNode = mGraphWidget->getContext()->getLayouter()->nodeAtPosition(mCurrentGridpos); + if (!targetNode.isNull()) + retval[targetNode] = mDragNodeBox->gridPosition(); + return retval; + } + if (!mAdditionalBoxes.isEmpty()) + { + QPoint delta = mCurrentGridpos - mDragNodeBox->gridPosition(); + for (const NodeBox* nb : mAdditionalBoxes) + { + retval[nb->getNode()] = nb->gridPosition() + delta; + } + } + return retval; + } +} diff --git a/plugins/gui/src/graph_widget/graph_graphics_view.cpp b/plugins/gui/src/graph_widget/graph_graphics_view.cpp index 95d7d28d940..0f9be329d54 100644 --- a/plugins/gui/src/graph_widget/graph_graphics_view.cpp +++ b/plugins/gui/src/graph_widget/graph_graphics_view.cpp @@ -2,6 +2,7 @@ #include "gui/comment_system/comment_speech_bubble.h" #include "gui/graph_widget/contexts/graph_context.h" +#include "gui/graph_widget/drag_controller.h" #include "gui/graph_widget/graph_widget.h" #include "gui/graph_widget/graph_widget_constants.h" #include "gui/graph_widget/graphics_scene.h" @@ -77,6 +78,7 @@ namespace hal : QGraphicsView(parent), mGraphWidget(parent), mMinimapEnabled(false), mGridEnabled(true), mGridClustersEnabled(true), mGridType(GraphicsScene::GridType::Dots), + mDragController(new DragController(mGraphWidget,this)), mDragModifier(Qt::KeyboardModifier::AltModifier), mPanModifier(Qt::KeyboardModifier::ShiftModifier), mZoomModifier(Qt::NoModifier), @@ -390,16 +392,14 @@ namespace hal } else if (event->button() == Qt::LeftButton) { - GraphicsItem* item = static_cast(itemAt(event->pos())); + GraphicsNode* item = dynamic_cast(itemAt(event->pos())); if (item && itemDraggable(item)) { - mDragItem = static_cast(item); - mDragMousedownPosition = event->pos(); - mDragStartGridpos = closestLayouterPos(mapToScene(mDragMousedownPosition)).first; + mDragController->set(item, event->pos()); } else { - mDragItem = nullptr; + mDragController->clear(); } // we still need the normal mouse logic for single clicks @@ -449,7 +449,7 @@ namespace hal } else if (event->buttons().testFlag(Qt::LeftButton)) { - if (mDragItem && (event->pos() - mDragMousedownPosition).manhattanLength() >= QApplication::startDragDistance()) + if (mDragController->hasDragged(event->pos())) { QDrag* drag = new QDrag(this); QMimeData* mimeData = new QMimeData; @@ -474,21 +474,8 @@ namespace hal { if (event->source() == this && event->proposedAction() == Qt::MoveAction) { + mDragController->enterDrag(event->keyboardModifiers() == mDragModifier); event->acceptProposedAction(); - QSizeF size(mDragItem->width(), mDragItem->height()); - QPointF mouse = event->posF(); - QPointF snap = closestLayouterPos(mapToScene(mouse.x(), mouse.y())).second; - if (gSelectionRelay->numberSelectedGates() > 1) - { - // if we are in multi-select mode, reduce the selection to the - // item we are dragging - gSelectionRelay->clear(); - gSelectionRelay->addGate(mDragItem->id()); - gSelectionRelay->setFocus(SelectionRelay::ItemType::Gate,mDragItem->id()); - gSelectionRelay->relaySelectionChanged(nullptr); - } - mDropAllowed = false; - static_cast(scene())->startDragShadow(snap, size, NodeDragShadow::DragCue::Rejected); } else { @@ -500,53 +487,16 @@ namespace hal void GraphGraphicsView::dragLeaveEvent(QDragLeaveEvent* event) { Q_UNUSED(event) - if (scene()) static_cast(scene())->stopDragShadow(); + mDragController->clear(); } void GraphGraphicsView::dragMoveEvent(QDragMoveEvent* event) { if (event->source() == this && event->proposedAction() == Qt::MoveAction) { - bool swapModifier = event->keyboardModifiers() == mDragModifier; QPair snap = closestLayouterPos(mapToScene(event->pos())); - if (snap.first == mDragCurrentGridpos && swapModifier == mDragCurrentModifier) - { - return; - } - mDragCurrentGridpos = snap.first; - mDragCurrentModifier = swapModifier; - - auto context = mGraphWidget->getContext(); - const GraphLayouter* layouter = context->getLayouter(); - assert(layouter->done()); // ensure grid stable - - QMap::const_iterator node_iter = layouter->positionToNodeMap().find(snap.first); - - NodeDragShadow::DragCue cue = NodeDragShadow::DragCue::Rejected; - // disallow dropping an item on itself - if (snap.first != mDragStartGridpos) - { - if (swapModifier) - { - if (node_iter != layouter->positionToNodeMap().end()) - { - // allow move only on empty cells - cue = NodeDragShadow::DragCue::Swappable; - } - } - else - { - if (node_iter == layouter->positionToNodeMap().end()) - { - // allow move only on empty cells - cue = NodeDragShadow::DragCue::Movable; - } - } - } - mDropAllowed = (cue != NodeDragShadow::DragCue::Rejected); - - static_cast(scene())->moveDragShadow(snap.second, cue); + mDragController->move(event->pos(),event->keyboardModifiers() == mDragModifier,snap.first); } } @@ -555,10 +505,10 @@ namespace hal if (event->source() == this && event->proposedAction() == Qt::MoveAction) { event->acceptProposedAction(); - GraphicsScene* s = static_cast(scene()); - if (s) s->stopDragShadow(); - if (mDropAllowed) + int alow; + if ((alow=mDragController->isDropAllowed()) == 0) { + /* auto context = mGraphWidget->getContext(); GraphLayouter* layouter = context->getLayouter(); assert(layouter->done()); // ensure grid stable @@ -579,12 +529,16 @@ namespace hal ActionMoveNode* act = new ActionMoveNode(context->id(), sourceLayouterPos, targetLayouterPos, modifierPressed); if (act->exec()) context->setDirty(true); + */ } + else + qDebug() << "drop not allowed" << alow; } else { QGraphicsView::dropEvent(event); } + mDragController->clear(); } void GraphGraphicsView::wheelEvent(QWheelEvent* event) diff --git a/plugins/gui/src/graph_widget/graphics_scene.cpp b/plugins/gui/src/graph_widget/graphics_scene.cpp index 78d40b965d3..9939e56e1db 100644 --- a/plugins/gui/src/graph_widget/graphics_scene.cpp +++ b/plugins/gui/src/graph_widget/graphics_scene.cpp @@ -74,7 +74,7 @@ namespace hal } GraphicsScene::GraphicsScene(QObject* parent) : QGraphicsScene(parent), - mDragShadowGate(new NodeDragShadow()), mDebugGridEnable(false), + mDebugGridEnable(false), mSelectionStatus(NotPressed) { // FIND OUT IF MANUAL CHANGE TO DEPTH IS NECESSARY / INCREASES PERFORMANCE @@ -84,7 +84,6 @@ namespace hal gSelectionRelay->registerSender(this, "GraphView"); connectAll(); - QGraphicsScene::addItem(mDragShadowGate); connect(gGraphContextManager->sSettingNetGroupingToPins,&SettingsItem::valueChanged,this,&GraphicsScene::updateAllItems); } @@ -98,28 +97,6 @@ namespace hal } } - void GraphicsScene::startDragShadow(const QPointF& posF, const QSizeF& sizeF, const NodeDragShadow::DragCue cue) - { - mDragShadowGate->setVisualCue(cue); - mDragShadowGate->start(posF, sizeF); - } - - void GraphicsScene::moveDragShadow(const QPointF& posF, const NodeDragShadow::DragCue cue) - { - mDragShadowGate->setPos(posF); - mDragShadowGate->setVisualCue(cue); - } - - void GraphicsScene::stopDragShadow() - { - mDragShadowGate->stop(); - } - - QPointF GraphicsScene::dropTarget() - { - return mDragShadowGate->pos(); - } - void GraphicsScene::addGraphItem(GraphicsItem* item) { // SELECTION HAS TO BE UPDATED MANUALLY AFTER ADDING / REMOVING ITEMS @@ -336,10 +313,7 @@ namespace hal // TODO check performance hit for (auto item : items()) { - if (item != mDragShadowGate) - { - removeItem(item); - } + removeItem(item); } mModuleItems.clear(); diff --git a/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp b/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp index 344d6c5ad7d..baddb22012b 100644 --- a/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp +++ b/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp @@ -36,28 +36,20 @@ namespace hal sPen.setJoinStyle(Qt::MiterJoin); } - NodeDragShadow::NodeDragShadow() : QGraphicsObject() + NodeDragShadow::NodeDragShadow() + : QGraphicsObject(), mRect(0,0,100,100) { - hide(); - setAcceptedMouseButtons(0); - mNodeShapes.append(QRectF(0,0,100,100)); } void NodeDragShadow::start(const QPointF& posF, const QSizeF& sizeF) { + mRect = QRectF(QPointF(0,0),sizeF); setPos(posF); - mNodeShapes.clear(); - mNodeShapes.append(QRectF(QPointF(0,0),sizeF)); setZValue(1); show(); } - void NodeDragShadow::stop() - { - hide(); - } - /* qreal NodeDragShadow::width() const { @@ -107,40 +99,27 @@ namespace hal sPen.setColor(sColorPen[color_index]); painter->setPen(sPen); - for (const QRectF& rect : mNodeShapes) + if (sLod < 0.5) { - if (sLod < 0.5) - { - - painter->fillRect(rect, sColorSolid[color_index]); - } - else - { - painter->drawRect(rect); - painter->fillRect(rect, sColorTranslucent[color_index]); - } + + painter->fillRect(mRect, sColorSolid[color_index]); + } + else + { + painter->drawRect(mRect); + painter->fillRect(mRect, sColorTranslucent[color_index]); } } QRectF NodeDragShadow::boundingRect() const { - QRectF retval; - if (mNodeShapes.isEmpty()) return retval; - for (const QRectF& rect : mNodeShapes) - { - if (retval.isNull()) - retval = rect; - else - retval = retval.united(rect); - } - return retval; + return mRect; } QPainterPath NodeDragShadow::shape() const { QPainterPath path; - for (const QRectF& rect : mNodeShapes) - path.addRect(rect); + path.addRect(mRect); return path; } } diff --git a/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp b/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp index 9071800273f..54d83ab3cda 100644 --- a/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp +++ b/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp @@ -68,6 +68,23 @@ namespace hal return mNodeToPositionMap; } + QHash GraphLayouter::nodeToPositionHash() const + { +#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) + QHash retval(mNodeToPositionMap.constBegin(),mNodeToPositionMap.constEnd()); +#else + QHash retval; + for (auto it=mNodeToPositionMap.constBegin(); it!=mNodeToPositionMap.constEnd(); ++it) + retval.insert(it.key(),it.value()); +#endif + return retval; + } + + Node GraphLayouter::nodeAtPosition(const QPoint& p) const + { + return mPositionToNodeMap.value(p); + } + NetLayoutPoint GraphLayouter::positonForNode(const Node& nd) const { if (nd.isNull()) return NetLayoutPoint(); From aaf514bbc5a26c9e965f13cd8a4d0cafbe2aa193 Mon Sep 17 00:00:00 2001 From: Skaleee <78816681+Skaleee@users.noreply.github.com> Date: Tue, 12 Dec 2023 20:37:30 +0100 Subject: [PATCH 37/66] Added Isolate net in new view to the context menu --- plugins/gui/src/module_widget/module_widget.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/gui/src/module_widget/module_widget.cpp b/plugins/gui/src/module_widget/module_widget.cpp index be8d6416189..bde1cf6be42 100644 --- a/plugins/gui/src/module_widget/module_widget.cpp +++ b/plugins/gui/src/module_widget/module_widget.cpp @@ -295,6 +295,9 @@ namespace hal extractPythonAction.setText("Extract Net as python code (copy to clipboard)"); extractPythonAction.setParent(&context_menu); + isolate_action.setText("Isolate in new view"); + isolate_action.setParent(&context_menu); + change_name_action.setText("Change Net name"); change_name_action.setParent(&context_menu); @@ -325,6 +328,7 @@ namespace hal if (type == ModuleItem::TreeItemType::Net){ context_menu.addAction(&extractPythonAction); + context_menu.addAction(&isolate_action); context_menu.addAction(&change_name_action); context_menu.addAction(&focus_in_view); } @@ -355,6 +359,7 @@ namespace hal { case ModuleItem::TreeItemType::Module: openModuleInView(index); break; case ModuleItem::TreeItemType::Gate: openGateInView(index); break; + case ModuleItem::TreeItemType::Net: openNetEndpointsInView(index); break; default: break; } From 6da119058be088b829f7e80d4de17506cff27f87 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 13 Dec 2023 14:19:00 +0100 Subject: [PATCH 38/66] Added pan function when dragging node to border of viewport --- .../gui/graph_widget/drag_controller.h | 6 +- .../gui/graph_widget/graph_graphics_view.h | 2 + .../graph_widget/layouters/graph_layouter.h | 7 +- .../gui/user_action/action_move_node.h | 2 +- .../gui/src/graph_widget/drag_controller.cpp | 150 ++++++++---------- .../src/graph_widget/graph_graphics_view.cpp | 60 ++++--- .../graph_widget/layouters/graph_layouter.cpp | 18 +-- .../gui/src/user_action/action_move_node.cpp | 2 + 8 files changed, 122 insertions(+), 125 deletions(-) diff --git a/plugins/gui/include/gui/graph_widget/drag_controller.h b/plugins/gui/include/gui/graph_widget/drag_controller.h index dad3077ed96..99b8bf0d4d8 100644 --- a/plugins/gui/include/gui/graph_widget/drag_controller.h +++ b/plugins/gui/include/gui/graph_widget/drag_controller.h @@ -37,6 +37,7 @@ namespace hal { class GraphicsNode; class NodeBox; class NodeDragShadow; + class GraphicsScene; class DragController : public QObject { @@ -49,6 +50,7 @@ namespace hal { QPoint mCurrentGridpos; QSet mAdditionalBoxes; QHash mShadows; + GraphicsScene* mShadowScene; void setSwapIntent(bool wantSwap); void addShadow(const NodeBox* nb); @@ -75,8 +77,8 @@ namespace hal { */ void move(const QPoint& eventPos, bool wantSwap, const QPoint& gridPos); bool hasDragged(const QPoint& eventPos); - int isDropAllowed() const; - QHash finalNodePositions() const; + bool isDropAllowed() const; + GridPlacement* finalGridPlacement() const; NodeDragShadow::DragCue dragCue() const; }; } diff --git a/plugins/gui/include/gui/graph_widget/graph_graphics_view.h b/plugins/gui/include/gui/graph_widget/graph_graphics_view.h index 2d3b5201bfd..424384b7c01 100644 --- a/plugins/gui/include/gui/graph_widget/graph_graphics_view.h +++ b/plugins/gui/include/gui/graph_widget/graph_graphics_view.h @@ -203,6 +203,8 @@ namespace hal void addSuccessorToView(int maxLevel, bool succ); void addCommonSuccessorToView(int maxLevel, bool succ); + void dragPan(float dpx, float dpy); + GraphWidget* mGraphWidget; QSet getSelectableGates(); diff --git a/plugins/gui/include/gui/graph_widget/layouters/graph_layouter.h b/plugins/gui/include/gui/graph_widget/layouters/graph_layouter.h index 367678f3b7d..6fd0498649b 100644 --- a/plugins/gui/include/gui/graph_widget/layouters/graph_layouter.h +++ b/plugins/gui/include/gui/graph_widget/layouters/graph_layouter.h @@ -271,7 +271,12 @@ namespace hal const QMap nodeToPositionMap() const; const QMap positionToNodeMap() const; - QHash nodeToPositionHash() const; + + /** + * Creates a new GridPlacement instance (node to position hash) and returns the pointer + * The caller has the ownership and responsibility to delete that instance + */ + GridPlacement* gridPlacementFactory() const; NetLayoutPoint positonForNode(const Node& nd) const; Node nodeAtPosition(const QPoint& p) const; diff --git a/plugins/gui/include/gui/user_action/action_move_node.h b/plugins/gui/include/gui/user_action/action_move_node.h index 47d7a7d2d6e..d2279d4e0ff 100644 --- a/plugins/gui/include/gui/user_action/action_move_node.h +++ b/plugins/gui/include/gui/user_action/action_move_node.h @@ -66,7 +66,7 @@ namespace hal * @param from - The initial position of the node to move * @param to - The destination of the node */ - ActionMoveNode(u32 ctxID, const QPoint& from, const QPoint& to, bool swap = false); + // ActionMoveNode(u32 ctxID, const QPoint& from, const QPoint& to, bool swap = false); /** * Action constructor. diff --git a/plugins/gui/src/graph_widget/drag_controller.cpp b/plugins/gui/src/graph_widget/drag_controller.cpp index e2cad6980e6..361842a2739 100644 --- a/plugins/gui/src/graph_widget/drag_controller.cpp +++ b/plugins/gui/src/graph_widget/drag_controller.cpp @@ -3,11 +3,10 @@ #include "gui/graph_widget/contexts/graph_context.h" #include "gui/graph_widget/graphics_scene.h" #include -#include namespace hal { DragController::DragController(GraphWidget* gw, QObject *parent) - : QObject(parent), mGraphWidget(gw), mDragNodeBox(nullptr) + : QObject(parent), mGraphWidget(gw), mDragNodeBox(nullptr), mShadowScene(nullptr) {;} void DragController::clear() @@ -17,10 +16,14 @@ namespace hal { mDropAllowed = false; mWantSwap = false; GraphicsScene* sc = mGraphWidget->getContext()->getLayouter()->scene(); - for (NodeDragShadow* nds : mShadows.values()) + if (sc && sc == mShadowScene) { - if (sc) sc->removeItem(nds); - delete nds; + // otherwise (if old scene deleted) items owned by scene already removed + for (NodeDragShadow* nds : mShadows.values()) + { + sc->removeItem(nds); + delete nds; + } } mShadows.clear(); } @@ -37,13 +40,26 @@ namespace hal { void DragController::set(GraphicsNode *drgItem, const QPoint &eventPos) { clear(); + if (!drgItem) return; // TODO: swap modifier -> deselect all but current - QList nodesToMove; + QSet nodesToMove; + switch (drgItem->itemType()) + { + case ItemType::Module: + nodesToMove.insert(Node(drgItem->id(),Node::Module)); + break; + case ItemType::Gate: + nodesToMove.insert(Node(drgItem->id(),Node::Gate)); + break; + default: + break; + } + for (u32 mid : gSelectionRelay->selectedModules()) - nodesToMove.append(Node(mid,Node::Module)); + nodesToMove.insert(Node(mid,Node::Module)); for (u32 gid : gSelectionRelay->selectedGates()) - nodesToMove.append(Node(gid,Node::Gate)); + nodesToMove.insert(Node(gid,Node::Gate)); auto context = mGraphWidget->getContext(); const GraphLayouter* layouter = context->getLayouter(); @@ -62,13 +78,29 @@ namespace hal { } if (!mAdditionalBoxes.isEmpty()) mWantSwap = false; - qDebug() << "drag set " << (mDragNodeBox ? mDragNodeBox->id() : -1) << mAdditionalBoxes.size(); } void DragController::setSwapIntent(bool wantSwap) { if (wantSwap == mWantSwap) return; - if (wantSwap) mAdditionalBoxes.clear(); + if (wantSwap) + { + GraphicsScene* sc = mGraphWidget->getContext()->getLayouter()->scene(); + if (sc && sc == mShadowScene) + { + for (NodeBox* nb : mAdditionalBoxes) + { + NodeDragShadow* nds = mShadows.value(nb); + if (nds) + { + sc->removeItem(nds); + mShadows.remove(nb); + delete nds; + } + } + } + mAdditionalBoxes.clear(); + } mWantSwap = wantSwap; } @@ -77,14 +109,18 @@ namespace hal { NodeDragShadow* nds = new NodeDragShadow; nds->setVisualCue(dragCue()); nds->start(nb->item()->pos(), nb->item()->boundingRect().size()); - GraphicsScene* sc = mGraphWidget->getContext()->getLayouter()->scene(); - if (sc) sc->addItem(nds); - mShadows.insert(nb,nds); + mShadowScene = mGraphWidget->getContext()->getLayouter()->scene(); + if (mShadowScene) + { + mShadowScene->addItem(nds); + mShadows.insert(nb,nds); + } + else + delete nds; } void DragController::enterDrag(bool wantSwap) { - qDebug() << "drag enter" << (mDragNodeBox ? mDragNodeBox->id() : -1) << mAdditionalBoxes.size(); if (!mDragNodeBox) return; setSwapIntent(wantSwap); mCurrentGridpos = mDragNodeBox->gridPosition(); @@ -100,9 +136,8 @@ namespace hal { if (!mDragNodeBox || (wantSwap == mWantSwap && gridPos == mCurrentGridpos)) return; setSwapIntent(wantSwap); - qDebug() << "drag set" << (mDragNodeBox ? mDragNodeBox->id() : -1) << mAdditionalBoxes.size(); mCurrentGridpos = gridPos; - mDropAllowed = (isDropAllowed()==0); + mDropAllowed = isDropAllowed(); QPoint delta = mCurrentGridpos - mDragNodeBox->gridPosition(); for (auto it = mShadows.constBegin(); it != mShadows.constEnd(); ++it) @@ -121,17 +156,15 @@ namespace hal { return (eventPos - mMousedownPosition).manhattanLength() >= QApplication::startDragDistance(); } - int DragController::isDropAllowed() const + bool DragController::isDropAllowed() const { - if (!mDragNodeBox) return -1; - if (mDragNodeBox->gridPosition() == mCurrentGridpos) return -2; + if (!mDragNodeBox) return false; + if (mDragNodeBox->gridPosition() == mCurrentGridpos) return false; const NodeBoxes& boxes = mGraphWidget->getContext()->getLayouter()->boxes(); if (mWantSwap) { - if (boxes.boxForPoint(mCurrentGridpos) != nullptr) - return 0; - return -3; + return (boxes.boxForPoint(mCurrentGridpos) != nullptr); } QList pointsToCheck; @@ -149,79 +182,20 @@ namespace hal { for (const QPoint& p : pointsToCheck) { if (freedPositions.contains(p)) continue; - if (boxes.boxForPoint(p) != nullptr) return -4; + if (boxes.boxForPoint(p) != nullptr) return false; } - return 0; + return true; } - /* - - auto context = mGraphWidget->getContext(); - const GraphLayouter* layouter = context->getLayouter(); - assert(layouter->done()); // ensure grid stable - - QMap::const_iterator node_iter = layouter->positionToNodeMap().find(mDragCurrentGridpos); - - NodeDragShadow::DragCue cue = NodeDragShadow::DragCue::Rejected; - // disallow dropping an item on itself - if (mDragCurrentGridpos != mDragStartGridpos) - { - if (swapModifier) - { - if (node_iter != layouter->positionToNodeMap().end()) - { - // allow move only on empty cells - cue = NodeDragShadow::DragCue::Swappable; - } - } - else - { - if (mDragAdditionalGridpos.isEmpty()) - { - // move single node - if (node_iter == layouter->positionToNodeMap().end()) - { - // allow move only on empty cells - cue = NodeDragShadow::DragCue::Movable; - } - } - else - { - // move multi nodes - if (node_iter == layouter->positionToNodeMap().end() || mDragAdditionalGridpos.contains(mDragCurrentGridpos) ) - { - cue = NodeDragShadow::DragCue::Movable; - for (QPoint p : mDragAdditionalGridpos) - { - p += mDragCurrentGridpos - mDragStartGridpos; - if (p != mDragStartGridpos && !mDragAdditionalGridpos.contains(p)) - { - if (layouter->positionToNodeMap().find(p) != layouter->positionToNodeMap().end()) - { - cue = NodeDragShadow::DragCue::Rejected; - break; - } - } - } - } - - } - } - } - mDropAllowed = (cue != NodeDragShadow::DragCue::Rejected); - - static_cast(scene())->moveDragShadow(snap.second, cue); - */ - - QHash DragController::finalNodePositions() const + GridPlacement *DragController::finalGridPlacement() const { - QHash retval = mGraphWidget->getContext()->getLayouter()->nodeToPositionHash(); - retval[mDragNodeBox->getNode()] = mCurrentGridpos; + GridPlacement* retval = mGraphWidget->getContext()->getLayouter()->gridPlacementFactory(); + retval->operator[](mDragNodeBox->getNode()) = mCurrentGridpos; if (mWantSwap) { Node targetNode = mGraphWidget->getContext()->getLayouter()->nodeAtPosition(mCurrentGridpos); if (!targetNode.isNull()) - retval[targetNode] = mDragNodeBox->gridPosition(); + retval->operator[](targetNode) = mDragNodeBox->gridPosition(); return retval; } if (!mAdditionalBoxes.isEmpty()) @@ -229,7 +203,7 @@ namespace hal { QPoint delta = mCurrentGridpos - mDragNodeBox->gridPosition(); for (const NodeBox* nb : mAdditionalBoxes) { - retval[nb->getNode()] = nb->gridPosition() + delta; + retval->operator[](nb->getNode()) = nb->gridPosition() + delta; } } return retval; diff --git a/plugins/gui/src/graph_widget/graph_graphics_view.cpp b/plugins/gui/src/graph_widget/graph_graphics_view.cpp index 0f9be329d54..bc0768fa47b 100644 --- a/plugins/gui/src/graph_widget/graph_graphics_view.cpp +++ b/plugins/gui/src/graph_widget/graph_graphics_view.cpp @@ -490,6 +490,26 @@ namespace hal mDragController->clear(); } + void GraphGraphicsView::dragPan(float dpx, float dpy) + { + if (dpx != 0) + { + QScrollBar* hBar = horizontalScrollBar(); + int hValue = hBar->value() + 10*dpx; + if (hValue < hBar->minimum()) hBar->setMinimum(hValue); + if (hValue > hBar->maximum()) hBar->setMaximum(hValue); + hBar->setValue(hValue); + } + if (dpy != 0) + { + QScrollBar* vBar = verticalScrollBar(); + int vValue = vBar->value() + 10*dpy; + if (vValue < vBar->minimum()) vBar->setMinimum(vValue); + if (vValue > vBar->maximum()) vBar->setMaximum(vValue); + vBar->setValue(vValue); + } + } + void GraphGraphicsView::dragMoveEvent(QDragMoveEvent* event) { if (event->source() == this && event->proposedAction() == Qt::MoveAction) @@ -497,6 +517,19 @@ namespace hal QPair snap = closestLayouterPos(mapToScene(event->pos())); mDragController->move(event->pos(),event->keyboardModifiers() == mDragModifier,snap.first); + + QPoint p = event->pos() - viewport()->geometry().topLeft(); + float rx = 100. * p.x() / viewport()->geometry().width(); + float ry = 100. * p.y() / viewport()->geometry().height(); +// qDebug() << "move it" << event->pos() << viewport()->geometry() << rx << ry; + float dpx = 0; + float dpy = 0; + if (rx < 10) dpx = -10+rx; + if (rx > 90) dpx = rx-90; + if (ry < 10) dpy = -10+ry; + if (ry > 90) dpy = ry-90; + if (dpx !=0 || dpy != 0) + dragPan(dpx, dpy); } } @@ -505,34 +538,17 @@ namespace hal if (event->source() == this && event->proposedAction() == Qt::MoveAction) { event->acceptProposedAction(); - int alow; - if ((alow=mDragController->isDropAllowed()) == 0) + if (mDragController->isDropAllowed()) { - /* - auto context = mGraphWidget->getContext(); + GridPlacement* plc = mDragController->finalGridPlacement(); + GraphContext* context = mGraphWidget->getContext(); GraphLayouter* layouter = context->getLayouter(); assert(layouter->done()); // ensure grid stable - - // convert scene coordinates into layouter grid coordinates - QPointF targetPos = s->dropTarget(); - QPoint targetLayouterPos = closestLayouterPos(targetPos).first; - QPoint sourceLayouterPos = layouter->gridPointByItem(mDragItem); - - if (targetLayouterPos == sourceLayouterPos) - { - qDebug() << "Attempted to drop gate onto itself, this should never happen!"; - return; - } - // assert(targetLayouterPos != sourceLayouterPos); - - bool modifierPressed = event->keyboardModifiers() == mDragModifier; - ActionMoveNode* act = new ActionMoveNode(context->id(), sourceLayouterPos, targetLayouterPos, modifierPressed); + ActionMoveNode* act = new ActionMoveNode(context->id(),plc); if (act->exec()) context->setDirty(true); - */ + delete plc; } - else - qDebug() << "drop not allowed" << alow; } else { diff --git a/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp b/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp index 54d83ab3cda..ae6eee941ee 100644 --- a/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp +++ b/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp @@ -68,15 +68,11 @@ namespace hal return mNodeToPositionMap; } - QHash GraphLayouter::nodeToPositionHash() const + GridPlacement *GraphLayouter::gridPlacementFactory() const { -#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) - QHash retval(mNodeToPositionMap.constBegin(),mNodeToPositionMap.constEnd()); -#else - QHash retval; + GridPlacement* retval = new GridPlacement(); for (auto it=mNodeToPositionMap.constBegin(); it!=mNodeToPositionMap.constEnd(); ++it) - retval.insert(it.key(),it.value()); -#endif + retval->insert(it.key(),it.value()); return retval; } @@ -211,10 +207,10 @@ namespace hal Q_ASSERT(!mXValues.isEmpty()); int inx = ix - mMinXIndex; if (inx < 0) - return mXValues[0] - inx * defaultGridWidth(); + return mXValues[0] + inx * defaultGridWidth(); if (inx < mXValues.size()) return mXValues[inx]; - return mXValues.last() + (inx - mXValues.size() - 1) * defaultGridWidth(); + return mXValues.last() + (inx - mXValues.size() + 1) * defaultGridWidth(); } qreal GraphLayouter::gridYposition(int iy) const @@ -222,10 +218,10 @@ namespace hal Q_ASSERT(!mYValues.isEmpty()); int inx = iy - mMinYIndex; if (inx < 0) - return mYValues[0] - inx * defaultGridHeight(); + return mYValues[0] + inx * defaultGridHeight(); if (inx < mYValues.size()) return mYValues[inx]; - return mYValues.last() + (inx - mYValues.size() - 1) * defaultGridHeight(); + return mYValues.last() + (inx - mYValues.size() + 1) * defaultGridHeight(); } void GraphLayouter::layout() diff --git a/plugins/gui/src/user_action/action_move_node.cpp b/plugins/gui/src/user_action/action_move_node.cpp index 2ea039cd8e1..17c8810b13d 100644 --- a/plugins/gui/src/user_action/action_move_node.cpp +++ b/plugins/gui/src/user_action/action_move_node.cpp @@ -52,6 +52,7 @@ namespace hal // At construction target position might still be occupied, so don't do any checks before exec() } + /* ActionMoveNode::ActionMoveNode(u32 ctxID, const QPoint& from, const QPoint& to, bool swap) : mContextId(ctxID), mTo(to), mSwap(swap) { @@ -86,6 +87,7 @@ namespace hal return; } } +*/ QString ActionMoveNode::tagname() const { From f4e88c9e258f15518f3b425b39eb072036774b7c Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 13 Dec 2023 19:56:34 +0100 Subject: [PATCH 39/66] added changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 459c0cf9721..b7ffbdac474 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. * refactored module widget * added option to show gate content for each module * added option to show interior nets for each module + * added `Isolate in new view` feature for nets * added button to expand or collapse all tree items * added delete module action and shortcut * added entries for context menu From 5bcc08c1e5a0fd376eee6315e6d6e890e4424102 Mon Sep 17 00:00:00 2001 From: Skaleee <78816681+Skaleee@users.noreply.github.com> Date: Fri, 22 Dec 2023 19:16:24 +0100 Subject: [PATCH 40/66] added model initialisation for show-grouping-content dialog --- plugins/gui/src/grouping/grouping_manager_widget.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/gui/src/grouping/grouping_manager_widget.cpp b/plugins/gui/src/grouping/grouping_manager_widget.cpp index 7c1354457b1..ecd13b1b3ae 100644 --- a/plugins/gui/src/grouping/grouping_manager_widget.cpp +++ b/plugins/gui/src/grouping/grouping_manager_widget.cpp @@ -503,6 +503,11 @@ namespace hal // Replace InputDialog with SelectionTreeView SelectionTreeView* selectionTreeView = new SelectionTreeView(&dialog, true); + SelectionTreeModel* selectionTreeModel = new SelectionTreeModel(this); // Need to fully initialise SelectionTreeView with a model + SelectionTreeProxyModel* selectionTreeProxyModel = new SelectionTreeProxyModel(this); + selectionTreeProxyModel->setSourceModel(selectionTreeModel); + selectionTreeView->setModel(selectionTreeProxyModel); + selectionTreeView->populate(true, grpId); QPushButton* closeButton = new QPushButton("Close", &dialog); From 47771f74b7eae073705290681940c87f712946aa Mon Sep 17 00:00:00 2001 From: joern274 Date: Tue, 2 Jan 2024 21:56:13 +0100 Subject: [PATCH 41/66] Catch out of range in stoull-function --- src/netlist/gate.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/netlist/gate.cpp b/src/netlist/gate.cpp index 9c1989a430a..b9226d46365 100644 --- a/src/netlist/gate.cpp +++ b/src/netlist/gate.cpp @@ -413,6 +413,16 @@ namespace hal config_str); return BooleanFunction(); } + catch (std::out_of_range& ex) + { + log_error("gate", + "LUT gate '{}' with ID {} in netlist with ID {} has invalid configuration string of '{}', which has to many hex digits.", + m_name, + m_id, + m_internal_manager->m_netlist->get_id(), + config_str); + return BooleanFunction(); + } u32 max_config_size = 1 << inputs.size(); From d77aa35d9900c815dc626b9b03ac6b17299d7cba Mon Sep 17 00:00:00 2001 From: joern274 Date: Sat, 6 Jan 2024 12:04:46 +0100 Subject: [PATCH 42/66] Exclude folded top_module from tests whether view is affected by core events --- .../include/gui/graph_widget/contexts/graph_context.h | 7 +++++++ .../gui/src/graph_widget/contexts/graph_context.cpp | 10 ++++++++++ plugins/gui/src/graph_widget/graph_context_manager.cpp | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/plugins/gui/include/gui/graph_widget/contexts/graph_context.h b/plugins/gui/include/gui/graph_widget/contexts/graph_context.h index 1691e6b4178..fa0d95a6891 100644 --- a/plugins/gui/include/gui/graph_widget/contexts/graph_context.h +++ b/plugins/gui/include/gui/graph_widget/contexts/graph_context.h @@ -223,6 +223,13 @@ namespace hal */ bool isShowingNetDestination(const u32 mNetId) const; + /** + * Checks whether there is only the folded top_module in the context + * which makes other time consumptive tests unnecessary + * @return true If here is only the folded top_module in the context + */ + bool isShowingFoldedTopModule() const; + /** * Given a net, this function returns the first visible source node. * diff --git a/plugins/gui/src/graph_widget/contexts/graph_context.cpp b/plugins/gui/src/graph_widget/contexts/graph_context.cpp index 17853eb8004..9dd970bcae7 100644 --- a/plugins/gui/src/graph_widget/contexts/graph_context.cpp +++ b/plugins/gui/src/graph_widget/contexts/graph_context.cpp @@ -321,6 +321,16 @@ namespace hal return contextGates == moduleGates && contextModules == moduleModules; } + + bool GraphContext::isShowingFoldedTopModule() const + { + auto contextGates = (mGates - mRemovedGates) + mAddedGates; + if (!contextGates.isEmpty()) return false; + auto contextModules = (mModules - mRemovedModules) + mAddedModules; + if (contextModules.size() != 1) return false; + return (*mModules.constBegin() == 1); // top_module has ID=1 + } + void GraphContext::getModuleChildrenRecursively(const u32 id, QSet* gates, QSet* modules) const { diff --git a/plugins/gui/src/graph_widget/graph_context_manager.cpp b/plugins/gui/src/graph_widget/graph_context_manager.cpp index 9f1b6dc4c55..254298b236a 100644 --- a/plugins/gui/src/graph_widget/graph_context_manager.cpp +++ b/plugins/gui/src/graph_widget/graph_context_manager.cpp @@ -233,6 +233,7 @@ namespace hal for (GraphContext* context : mContextTableModel->list()) { + if (context->isShowingFoldedTopModule()) continue; if (context->isShowingModule(m->get_id(), {added_module}, {}, {}, {}) && !context->isShowingModule(added_module, {}, {}, {}, {})) context->add({added_module}, {}); else @@ -267,6 +268,7 @@ namespace hal for (GraphContext* context : mContextTableModel->list()) { + if (context->isShowingFoldedTopModule()) continue; if (context->isScheduledRemove(Node(removed_module,Node::Module)) || context->isShowingModule(m->get_id(), {}, {}, {removed_module}, {})) context->remove({removed_module}, {}); @@ -293,6 +295,7 @@ namespace hal for (GraphContext* context : mContextTableModel->list()) { + if (context->isShowingFoldedTopModule()) continue; if (context->isShowingModule(m->get_id(), {}, {inserted_gate}, {}, {})) context->add({}, {inserted_gate}); else @@ -317,6 +320,7 @@ namespace hal // dump("ModuleGateRemoved", m->get_id(), removed_gate); for (GraphContext* context : mContextTableModel->list()) { + if (context->isShowingFoldedTopModule()) continue; if (context->isScheduledRemove(Node(removed_gate,Node::Gate)) || context->isShowingModule(m->get_id(), {}, {}, {}, {removed_gate})) { From f084a8b7b7842ef72a4035664e980e15ee34e2e7 Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 15 Jan 2024 15:10:18 +0100 Subject: [PATCH 43/66] added macro for VALGRIND profiling --- include/hal_core/netlist/netlist.h | 1 + src/netlist/netlist.cpp | 10 ++++++++++ src/python_bindings/bindings/netlist.cpp | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/hal_core/netlist/netlist.h b/include/hal_core/netlist/netlist.h index 415db8badbf..4ecadfc8006 100644 --- a/include/hal_core/netlist/netlist.h +++ b/include/hal_core/netlist/netlist.h @@ -473,6 +473,7 @@ namespace hal * @returns The new module on success, nullptr otherwise. */ Module* create_module(const std::string& name, Module* parent, const std::vector& gates = {}); + Module* create_module_python(const std::string& name, Module* parent, const std::vector& gates = {}); /** * Remove a module from the netlist. diff --git a/src/netlist/netlist.cpp b/src/netlist/netlist.cpp index 24a364b8992..bc73520dd93 100644 --- a/src/netlist/netlist.cpp +++ b/src/netlist/netlist.cpp @@ -7,6 +7,7 @@ #include "hal_core/netlist/net.h" #include "hal_core/netlist/netlist_internal_manager.h" #include "hal_core/utilities/log.h" +#include namespace hal { @@ -566,6 +567,15 @@ namespace hal return create_module(get_unique_module_id(), name, parent, gates); } + Module* Netlist::create_module_python(const std::string &name, Module *parent, const std::vector &gates) + { + CALLGRIND_START_INSTRUMENTATION; + CALLGRIND_TOGGLE_COLLECT; + return create_module(get_unique_module_id(), name, parent, gates); + CALLGRIND_TOGGLE_COLLECT; + CALLGRIND_STOP_INSTRUMENTATION; + } + bool Netlist::delete_module(Module* module) { return m_manager->delete_module(module); diff --git a/src/python_bindings/bindings/netlist.cpp b/src/python_bindings/bindings/netlist.cpp index 3b30eb2b882..1a15960835a 100644 --- a/src/python_bindings/bindings/netlist.cpp +++ b/src/python_bindings/bindings/netlist.cpp @@ -500,7 +500,7 @@ namespace hal )"); py_netlist.def("create_module", - py::overload_cast&>(&Netlist::create_module), + &Netlist::create_module_python, py::arg("name"), py::arg("parent"), py::arg("gates") = std::vector(), From 2ed2c7798c51f4b8b761bd9a99b26beae69d1686 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 17 Jan 2024 21:35:31 +0100 Subject: [PATCH 44/66] 1.ModuleModel instance moved to ModuleWidget 2.net assignment accelerated --- .../include/gui/module_model/module_model.h | 48 +++-- .../include/gui/module_widget/module_widget.h | 9 + .../include/gui/netlist_relay/netlist_relay.h | 9 - .../gui/src/module_dialog/module_dialog.cpp | 3 +- plugins/gui/src/module_model/module_model.cpp | 167 ++++++++++++++++-- .../gui/src/module_widget/module_widget.cpp | 27 ++- .../gui/src/netlist_relay/netlist_relay.cpp | 25 +-- 7 files changed, 228 insertions(+), 60 deletions(-) diff --git a/plugins/gui/include/gui/module_model/module_model.h b/plugins/gui/include/gui/module_model/module_model.h index 11086efdb05..edd4ecd76e8 100644 --- a/plugins/gui/include/gui/module_model/module_model.h +++ b/plugins/gui/include/gui/module_model/module_model.h @@ -171,15 +171,6 @@ namespace hal */ void removeNet(const u32 id); - /** - * Moves the ModuleItem corresponding to the module under it's new parent ModuleItem. - * The items for all nets, that have at least one source or one destination within the module, - * will be updated afterwards. - * - * @param module The module whose parent has changed. - */ - void handleModuleParentChanged(const Module* module); - /** * Handles the assigment of gates to modules. * If the gate does not yet exist in the item model, a new one is created. @@ -195,7 +186,7 @@ namespace hal * * @param net The net whose source or destination might have changed. */ - void updateNet(const Net* net); + void updateNet(const Net* net, const QHash* parentAssignment = nullptr); /** * Reattaches the ModuleItem corresponding to the specified module to a new parent item. @@ -235,6 +226,41 @@ namespace hal */ bool isModifying(); + void debugDump() const; + + private Q_SLOTS: + void handleModuleNameChanged(Module* mod); + + void handleModuleRemoved(Module* mod); + + void handleModuleCreated(Module* mod); + + /** + * Moves the ModuleItem corresponding to the module under it's new parent ModuleItem. + * The items for all nets, that have at least one source or one destination within the module, + * will be updated afterwards. + * + * @param module The module whose parent has changed. + */ + void handleModuleParentChanged(const Module* module); + + void handleModuleGateAssigned(Module* mod, u32 gateId); + + void handleModuleGateRemoved(Module* mod, u32 gateId); + + void handleModuleGatesAssignBegin(Module* mod, u32 numberGates); + + void handleModuleGatesAssignEnd(Module* mod, u32 numberGates); + + void handleGateNameChanged(Gate* gat); + + void handleNetCreated(Net* net); + + void handleNetRemoved(Net* net); + + void handleNetNameChanged(Net* net); + + void handleNetUpdated(Net* net, u32 data); private: /** * Searches for a new parent module, such that it is the deepest module in the hierarchy, that contains all @@ -246,6 +272,8 @@ namespace hal * (e.g. net has no sources or destinations), nullptr is returned instead. */ Module* findNetParent(const Net* net); + void updateAllNets(); + void findNetParentRecursion(BaseTreeItem* parent, QHash& parentAssignment, std::unordered_set& assignedNets) const; QMap mModuleMap; QMap mGateMap; diff --git a/plugins/gui/include/gui/module_widget/module_widget.h b/plugins/gui/include/gui/module_widget/module_widget.h index fb18026a2c7..115a6e7199c 100644 --- a/plugins/gui/include/gui/module_widget/module_widget.h +++ b/plugins/gui/include/gui/module_widget/module_widget.h @@ -45,6 +45,7 @@ class QTreeView; namespace hal { + class ModuleModel; class ModuleProxyModel; /** @@ -93,6 +94,13 @@ namespace hal */ virtual QList createShortcuts() override; + /** + * Accesses the module model. + * + * @returns the module model + */ + ModuleModel* getModuleModel() const; + /** * Opens a existing view that contains the given module, otherwise creates a new context * and opens it. @@ -268,6 +276,7 @@ namespace hal bool mIgnoreSelectionChange; + ModuleModel* mModuleModel; ModuleProxyModel* mModuleProxyModel; QShortcut* mShortCutDeleteItem; diff --git a/plugins/gui/include/gui/netlist_relay/netlist_relay.h b/plugins/gui/include/gui/netlist_relay/netlist_relay.h index 84be264ba83..e8cc5c3275e 100644 --- a/plugins/gui/include/gui/netlist_relay/netlist_relay.h +++ b/plugins/gui/include/gui/netlist_relay/netlist_relay.h @@ -35,7 +35,6 @@ namespace hal { class ModuleItem; - class ModuleModel; class ModuleColorManager; class ModuleColorSerializer; class Module; @@ -87,13 +86,6 @@ namespace hal */ QColor getModuleColor(const u32 id); - /** - * Accesses the module model. - * - * @returns the module model - */ - ModuleModel* getModuleModel() const; - /** * Accesses the module color manager * @@ -620,7 +612,6 @@ namespace hal bool mNotified; QMap mModuleColors; - ModuleModel* mModuleModel; ModuleColorManager* mModuleColorManager; ModuleColorSerializer mColorSerializer; enum ThreadEventType { TetNetlist, TetModule, TetGate, TetNet, TetGrouping }; diff --git a/plugins/gui/src/module_dialog/module_dialog.cpp b/plugins/gui/src/module_dialog/module_dialog.cpp index c2b58d9c436..f3502409b54 100644 --- a/plugins/gui/src/module_dialog/module_dialog.cpp +++ b/plugins/gui/src/module_dialog/module_dialog.cpp @@ -4,6 +4,7 @@ #include "gui/module_model/module_model.h" #include "gui/module_model/module_proxy_model.h" #include "gui/module_model/module_item.h" +#include "gui/module_widget/module_widget.h" #include "gui/graph_tab_widget/graph_tab_widget.h" #include "gui/searchbar/searchbar.h" #include "gui/content_manager/content_manager.h" @@ -81,7 +82,7 @@ namespace hal { mModuleTreeProxyModel = new ModuleProxyModel(this); mModuleTreeProxyModel->setFilterKeyColumn(-1); mModuleTreeProxyModel->setDynamicSortFilter(true); - mModuleTreeProxyModel->setSourceModel(gNetlistRelay->getModuleModel()); + mModuleTreeProxyModel->setSourceModel(gContentManager->getModuleWidget()->getModuleModel()); mTreeView->setModel(mModuleTreeProxyModel); mTreeView->expandAll(); diff --git a/plugins/gui/src/module_model/module_model.cpp b/plugins/gui/src/module_model/module_model.cpp index 7c645b97364..103c8784dc6 100644 --- a/plugins/gui/src/module_model/module_model.cpp +++ b/plugins/gui/src/module_model/module_model.cpp @@ -13,6 +13,22 @@ namespace hal { // use root item to store header information setHeaderLabels(QStringList() << "Name" << "ID" << "Type"); + connect(gNetlistRelay, &NetlistRelay::moduleCreated, this, &ModuleModel::handleModuleCreated); + connect(gNetlistRelay, &NetlistRelay::moduleNameChanged, this, &ModuleModel::handleModuleNameChanged); + connect(gNetlistRelay, &NetlistRelay::moduleParentChanged, this, &ModuleModel::handleModuleParentChanged); + connect(gNetlistRelay, &NetlistRelay::moduleGateAssigned, this, &ModuleModel::handleModuleGateAssigned); + connect(gNetlistRelay, &NetlistRelay::moduleGatesAssignBegin, this, &ModuleModel::handleModuleGatesAssignBegin); + connect(gNetlistRelay, &NetlistRelay::moduleGatesAssignEnd, this, &ModuleModel::handleModuleGatesAssignEnd); + connect(gNetlistRelay, &NetlistRelay::moduleGateRemoved, this, &ModuleModel::handleModuleGateRemoved); + connect(gNetlistRelay, &NetlistRelay::moduleRemoved, this, &ModuleModel::handleModuleRemoved); + connect(gNetlistRelay, &NetlistRelay::gateNameChanged, this, &ModuleModel::handleGateNameChanged); + connect(gNetlistRelay, &NetlistRelay::netCreated, this, &ModuleModel::handleNetCreated); + connect(gNetlistRelay, &NetlistRelay::netRemoved, this, &ModuleModel::handleNetRemoved); + connect(gNetlistRelay, &NetlistRelay::netNameChanged, this, &ModuleModel::handleNetNameChanged); + connect(gNetlistRelay, &NetlistRelay::netSourceAdded, this, &ModuleModel::handleNetUpdated); + connect(gNetlistRelay, &NetlistRelay::netSourceRemoved, this, &ModuleModel::handleNetUpdated); + connect(gNetlistRelay, &NetlistRelay::netDestinationAdded, this, &ModuleModel::handleNetUpdated); + connect(gNetlistRelay, &NetlistRelay::netDestinationRemoved, this, &ModuleModel::handleNetUpdated); } QVariant ModuleModel::data(const QModelIndex& index, int role) const @@ -255,6 +271,71 @@ namespace hal delete item; } + void ModuleModel::handleModuleNameChanged(Module* mod) + { + updateModuleName(mod->get_id()); + } + + void ModuleModel::handleModuleRemoved(Module* mod) + { + removeModule(mod->get_id()); + } + + void ModuleModel::handleModuleCreated(Module* mod) + { + if (mod->get_parent_module() == nullptr) return; + addModule(mod->get_id(), mod->get_parent_module()->get_id()); + } + + void ModuleModel::handleModuleGateAssigned(Module* mod, u32 gateId) + { + handleModuleGateAssinged(gateId, mod->get_id()); + } + + void ModuleModel::handleModuleGateRemoved(Module* mod, u32 gateId) + { + Q_UNUSED(mod); + removeGate(gateId); + } + + void ModuleModel::handleModuleGatesAssignBegin(Module* mod, u32 numberGates) + { + Q_UNUSED(mod); + Q_UNUSED(numberGates); + } + + void ModuleModel::handleModuleGatesAssignEnd(Module* mod, u32 numberGates) + { + Q_UNUSED(mod); + Q_UNUSED(numberGates); + } + + void ModuleModel::handleGateNameChanged(Gate* gat) + { + updateGateName(gat->get_id()); + } + + void ModuleModel::handleNetCreated(Net* net) + { + addNet(net->get_id(), gNetlist->get_top_module()->get_id()); + } + + void ModuleModel::handleNetRemoved(Net* net) + { + removeNet(net->get_id()); + } + + void ModuleModel::handleNetNameChanged(Net* net) + { + updateNetName(net->get_id()); + } + + void ModuleModel::handleNetUpdated(Net* net, u32 data) + { + Q_UNUSED(data); + updateNet(net); + } + void ModuleModel::removeNet(const u32 id) { //assert(gNetlist->get_net_by_id(id)); @@ -289,6 +370,54 @@ namespace hal updateNet(net); } + void ModuleModel::findNetParentRecursion(BaseTreeItem* parent, QHash &parentAssignment, std::unordered_set& assignedNets) const + { + for (BaseTreeItem* bti : parent->getChildren()) + { + ModuleItem* item = dynamic_cast(bti); + if (!item || item->getType() != ModuleItem::TreeItemType::Module) continue; + findNetParentRecursion(item, parentAssignment, assignedNets); + Module* m = gNetlist->get_module_by_id(item->id()); + Q_ASSERT(m); + std::unordered_set internalNets = m->get_internal_nets(); + if (!internalNets.empty()) + { + for (Net* n : assignedNets) + internalNets.erase(n); + for (Net* n : m->get_input_nets()) + internalNets.erase(n); + for (Net* n : m->get_output_nets()) + internalNets.erase(n); + } + for (Net* n : internalNets) + { + parentAssignment[n] = item; + assignedNets.insert(n); + } + } + } + + void ModuleModel::debugDump() const + { + QHash parentAssignment; + std::unordered_set assignedNets; + findNetParentRecursion(mRootItem, parentAssignment, assignedNets); + + QTextStream xout(stdout, QIODevice::WriteOnly); + for (auto it = mModuleMap.begin(); it != mModuleMap.end(); ++it) + { + xout << it.value()->id() << " " << it.value()->name() << " "; + + for (auto jt = parentAssignment.constBegin(); jt != parentAssignment.constEnd(); ++jt) + { + if (it.value() != jt.value()) continue; + xout << " <" << jt.key()->get_id() << ">"; + } + xout << "\n"; + } + xout.flush(); + } + void ModuleModel::handleModuleGateAssinged(const u32 id, const u32 parent_module) { // Don't need new function handleModuleGateRemoved(), because the GateAssinged event always follows GateRemoved @@ -296,15 +425,19 @@ namespace hal if(!mGateMap.contains(id)) addGate(id, parent_module); - + + QHash parentAssignment; + std::unordered_set assignedNets; + findNetParentRecursion(mRootItem, parentAssignment, assignedNets); + Gate* gate = gNetlist->get_gate_by_id(id); for(Net* in_net : gate->get_fan_in_nets()) - updateNet(in_net); + updateNet(in_net, &parentAssignment); for(Net* in_net : gate->get_fan_out_nets()) - updateNet(in_net); + updateNet(in_net, &parentAssignment); } - void ModuleModel::updateNet(const Net* net) + void ModuleModel::updateNet(const Net* net, const QHash *parentAssignment) { assert(net); u32 id = net->get_id(); @@ -316,14 +449,28 @@ namespace hal ModuleItem* oldParentItem = static_cast(item->getParent()); assert(oldParentItem); - Module* newParentModule = findNetParent(net); - if(newParentModule == nullptr) - newParentModule = gNetlist->get_top_module(); - if(newParentModule->get_id() == oldParentItem->id()) + ModuleItem* newParentItem = nullptr; + if (parentAssignment) + newParentItem = parentAssignment->value(net); + else + { + Module* newParentModule = findNetParent(net); + if(newParentModule == nullptr) + newParentModule = gNetlist->get_top_module(); + newParentItem = mModuleMap[newParentModule->get_id()]; + } + + if (!newParentItem) + { + mIsModifying = true; + oldParentItem->removeChild(item); + mIsModifying = false; + return; + } + + if(newParentItem->id() == oldParentItem->id()) return; - assert(mModuleMap.contains(newParentModule->get_id())); - ModuleItem* newParentItem = mModuleMap[newParentModule->get_id()]; QModelIndex newIndex = getIndex(newParentItem); QModelIndex oldIndex = getIndex(oldParentItem); int row = item->row(); diff --git a/plugins/gui/src/module_widget/module_widget.cpp b/plugins/gui/src/module_widget/module_widget.cpp index bde1cf6be42..aaf294f42bc 100644 --- a/plugins/gui/src/module_widget/module_widget.cpp +++ b/plugins/gui/src/module_widget/module_widget.cpp @@ -62,7 +62,8 @@ namespace hal mRenameAction->setToolTip("Rename"); mToggleExpandTreeAction->setToolTip("Toggle expand all / collapse all"); - mModuleProxyModel->setSourceModel(gNetlistRelay->getModuleModel()); + mModuleModel = new ModuleModel(this); + mModuleProxyModel->setSourceModel(mModuleModel); mTreeView->setModel(mModuleProxyModel); mTreeView->setDefaultColumnWidth(); @@ -78,7 +79,7 @@ namespace hal mTreeView->expandAllModules(); mContentLayout->addWidget(mTreeView); - mSearchbar->setColumnNames(gNetlistRelay->getModuleModel()->headerLabels()); + mSearchbar->setColumnNames(mModuleModel->headerLabels()); mContentLayout->addWidget(mSearchbar); mSearchbar->hide(); @@ -91,7 +92,7 @@ namespace hal connect(mTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ModuleWidget::handleTreeSelectionChanged); connect(mTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModuleWidget::handleCurrentChanged); connect(mTreeView, &ModuleTreeView::doubleClicked, this, &ModuleWidget::handleItemDoubleClicked); - connect(gSelectionRelay, &SelectionRelay::selectionChanged, this, &ModuleWidget::handleSelectionChanged); + connect(gSelectionRelay, &SelectionRelay::selectionChanged, this, &ModuleWidget::handleSelectionChanged, Qt::QueuedConnection); connect(gNetlistRelay, &NetlistRelay::moduleSubmoduleRemoved, this, &ModuleWidget::handleModuleRemoved); connect(mSearchAction, &QAction::triggered, this, &ModuleWidget::toggleSearchbar); @@ -113,6 +114,8 @@ namespace hal connect(mToggleGatesAction, &QAction::triggered, this, &ModuleWidget::handleToggleGatesClicked); connect(mToggleExpandTreeAction, &QAction::triggered, this, &ModuleWidget::handleToggleExpandTreeClicked); connect(mRenameAction, &QAction::triggered, this, &ModuleWidget::handleRenameClicked); + + mModuleModel->init(); } void ModuleWidget::enableDeleteAction(bool enable) @@ -242,6 +245,10 @@ namespace hal QAction delete_action; QAction extractPythonAction; QAction focus_in_view; + QAction debug_dump; + debug_dump.setText("Debug dump"); + debug_dump.setParent(&context_menu); + context_menu.addAction(&debug_dump); switch(type) { @@ -384,6 +391,9 @@ namespace hal } } + if (clicked == &debug_dump) + mModuleModel->debugDump(); + if (clicked == &change_type_action) gNetlistRelay->changeModuleType(getModuleItemFromIndex(index)->id()); @@ -432,7 +442,7 @@ namespace hal Q_UNUSED(selected) Q_UNUSED(deselected) - if (mIgnoreSelectionChange || gNetlistRelay->getModuleModel()->isModifying()) + if (mIgnoreSelectionChange || mModuleModel->isModifying()) return; gSelectionRelay->clear(); @@ -608,7 +618,7 @@ namespace hal for (auto module_id : gSelectionRelay->selectedModulesList()) { - QModelIndex index = mModuleProxyModel->mapFromSource(gNetlistRelay->getModuleModel()->getIndex(gNetlistRelay->getModuleModel()->getItem(module_id))); + QModelIndex index = mModuleProxyModel->mapFromSource(mModuleModel->getIndex(mModuleModel->getItem(module_id))); module_selection.select(index, index); } @@ -619,7 +629,7 @@ namespace hal ModuleItem* ModuleWidget::getModuleItemFromIndex(const QModelIndex& index) { - return gNetlistRelay->getModuleModel()->getItem(mModuleProxyModel->mapToSource(index)); + return mModuleModel->getItem(mModuleProxyModel->mapToSource(index)); } void ModuleWidget::updateSearchIcon() @@ -630,6 +640,11 @@ namespace hal mSearchAction->setIcon(gui_utility::getStyledSvgIcon(mSearchIconStyle, mSearchIconPath)); } + ModuleModel* ModuleWidget::getModuleModel() const + { + return mModuleModel; + } + QString ModuleWidget::disabledIconStyle() const { return mDisabledIconStyle; diff --git a/plugins/gui/src/netlist_relay/netlist_relay.cpp b/plugins/gui/src/netlist_relay/netlist_relay.cpp index 8473b1bf209..541d46e2655 100644 --- a/plugins/gui/src/netlist_relay/netlist_relay.cpp +++ b/plugins/gui/src/netlist_relay/netlist_relay.cpp @@ -28,7 +28,7 @@ namespace hal { NetlistRelay::NetlistRelay(QObject* parent) - : QObject(parent), mModuleModel(new ModuleModel(this)), mModuleColorManager(new ModuleColorManager(this)) + : QObject(parent), mModuleColorManager(new ModuleColorManager(this)) { connect(FileManager::get_instance(), &FileManager::fileOpened, this, &NetlistRelay::debugHandleFileOpened); // DEBUG LINE connect(this, &NetlistRelay::signalThreadEvent, this, &NetlistRelay::handleThreadEvent, Qt::BlockingQueuedConnection); @@ -85,11 +85,6 @@ namespace hal return mModuleColorManager->moduleColor(id); } - ModuleModel* NetlistRelay::getModuleModel() const - { - return mModuleModel; - } - ModuleColorManager* NetlistRelay::getModuleColorManager() const { return mModuleColorManager; @@ -380,7 +375,6 @@ namespace hal // suppress actions if we receive this for the top module if (mod->get_parent_module() != nullptr) { - mModuleModel->addModule(mod->get_id(), mod->get_parent_module()->get_id()); mModuleColorManager->setRandomColor(mod->get_id()); } @@ -393,7 +387,6 @@ namespace hal //< no associated_data mModuleColorManager->removeColor(mod->get_id()); - mModuleModel->removeModule(mod->get_id()); gGraphContextManager->handleModuleRemoved(mod); gSelectionRelay->handleModuleRemoved(mod->get_id()); @@ -404,8 +397,6 @@ namespace hal case ModuleEvent::event::name_changed: { //< no associated_data - mModuleModel->updateModuleName(mod->get_id()); - gGraphContextManager->handleModuleNameChanged(mod); Q_EMIT moduleNameChanged(mod); @@ -414,8 +405,6 @@ namespace hal case ModuleEvent::event::parent_changed: { //< no associated_data - mModuleModel->handleModuleParentChanged(mod); - Q_EMIT moduleParentChanged(mod); break; } @@ -438,7 +427,6 @@ namespace hal case ModuleEvent::event::gate_assigned: { //< associated_data = id of inserted gate - mModuleModel->handleModuleGateAssinged(associated_data, mod->get_id()); gGraphContextManager->handleModuleGateAssigned(mod, associated_data); Q_EMIT moduleGateAssigned(mod, associated_data); @@ -447,7 +435,6 @@ namespace hal case ModuleEvent::event::gate_removed: { //< associated_data = id of removed gate - mModuleModel->removeGate(associated_data); gGraphContextManager->handleModuleGateRemoved(mod, associated_data); Q_EMIT moduleGateRemoved(mod, associated_data); @@ -536,7 +523,6 @@ namespace hal case GateEvent::event::name_changed: { //< no associated_data - mModuleModel->updateGateName(gat->get_id()); gGraphContextManager->handleGateNameChanged(gat); Q_EMIT gateNameChanged(gat); @@ -581,7 +567,6 @@ namespace hal case NetEvent::event::created: { //< no associated_data - mModuleModel->addNet(net->get_id(), gNetlist->get_top_module()->get_id()); gGraphContextManager->handleNetCreated(net); Q_EMIT netCreated(net); @@ -590,7 +575,6 @@ namespace hal case NetEvent::event::removed: { //< no associated_data - mModuleModel->removeNet(net->get_id()); gGraphContextManager->handleNetRemoved(net); gSelectionRelay->handleNetRemoved(net->get_id()); @@ -600,7 +584,6 @@ namespace hal case NetEvent::event::name_changed: { //< no associated_data - mModuleModel->updateNetName(net->get_id()); gGraphContextManager->handleNetNameChanged(net); Q_EMIT netNameChanged(net); @@ -619,7 +602,6 @@ namespace hal case NetEvent::event::src_added: { //< associated_data = id of src gate - mModuleModel->updateNet(net); gGraphContextManager->handleNetSourceAdded(net, associated_data); Q_EMIT netSourceAdded(net, associated_data); @@ -628,7 +610,6 @@ namespace hal case NetEvent::event::src_removed: { //< associated_data = id of src gate - mModuleModel->updateNet(net); gGraphContextManager->handleNetSourceRemoved(net, associated_data); Q_EMIT netSourceRemoved(net, associated_data); @@ -637,7 +618,6 @@ namespace hal case NetEvent::event::dst_added: { //< associated_data = id of dst gate - mModuleModel->updateNet(net); gGraphContextManager->handleNetDestinationAdded(net, associated_data); Q_EMIT netDestinationAdded(net, associated_data); @@ -646,7 +626,6 @@ namespace hal case NetEvent::event::dst_removed: { //< associated_data = id of dst gate - mModuleModel->updateNet(net); gGraphContextManager->handleNetDestinationRemoved(net, associated_data); Q_EMIT netDestinationRemoved(net, associated_data); @@ -686,12 +665,10 @@ namespace hal { for (Module* m : gNetlist->get_modules()) mModuleColorManager->setRandomColor(m->get_id()); - mModuleModel->init(); mColorSerializer.restore(mModuleColorManager); } void NetlistRelay::debugHandleFileClosed() { - mModuleModel->clear(); } } // namespace hal From cb0b70450101377127bad3b8cece0a8c53507943 Mon Sep 17 00:00:00 2001 From: nils1603 Date: Fri, 19 Jan 2024 14:36:30 +0100 Subject: [PATCH 45/66] added rapidjson to deps (#552) * added rapidjson to deps * added version info rapidjson --- cmake/detect_dependencies.cmake | 43 +- deps/rapidjson/rapidjson/allocators.h | 693 ++++ .../rapidjson/rapidjson/cursorstreamwrapper.h | 78 + deps/rapidjson/rapidjson/document.h | 3043 +++++++++++++++ deps/rapidjson/rapidjson/encodedstream.h | 299 ++ deps/rapidjson/rapidjson/encodings.h | 716 ++++ deps/rapidjson/rapidjson/error/en.h | 176 + deps/rapidjson/rapidjson/error/error.h | 285 ++ deps/rapidjson/rapidjson/filereadstream.h | 99 + deps/rapidjson/rapidjson/filewritestream.h | 104 + deps/rapidjson/rapidjson/fwd.h | 151 + .../rapidjson/rapidjson/internal/biginteger.h | 297 ++ deps/rapidjson/rapidjson/internal/clzll.h | 71 + deps/rapidjson/rapidjson/internal/diyfp.h | 261 ++ deps/rapidjson/rapidjson/internal/dtoa.h | 249 ++ deps/rapidjson/rapidjson/internal/ieee754.h | 78 + deps/rapidjson/rapidjson/internal/itoa.h | 308 ++ deps/rapidjson/rapidjson/internal/meta.h | 186 + deps/rapidjson/rapidjson/internal/pow10.h | 55 + deps/rapidjson/rapidjson/internal/regex.h | 739 ++++ deps/rapidjson/rapidjson/internal/stack.h | 232 ++ deps/rapidjson/rapidjson/internal/strfunc.h | 83 + deps/rapidjson/rapidjson/internal/strtod.h | 293 ++ deps/rapidjson/rapidjson/internal/swap.h | 46 + deps/rapidjson/rapidjson/istreamwrapper.h | 128 + deps/rapidjson/rapidjson/memorybuffer.h | 70 + deps/rapidjson/rapidjson/memorystream.h | 71 + .../rapidjson/rapidjson/msinttypes/inttypes.h | 316 ++ deps/rapidjson/rapidjson/msinttypes/stdint.h | 300 ++ deps/rapidjson/rapidjson/ostreamwrapper.h | 81 + deps/rapidjson/rapidjson/pointer.h | 1476 ++++++++ deps/rapidjson/rapidjson/prettywriter.h | 277 ++ deps/rapidjson/rapidjson/rapidjson.h | 741 ++++ deps/rapidjson/rapidjson/reader.h | 2246 ++++++++++++ deps/rapidjson/rapidjson/schema.h | 3262 +++++++++++++++++ deps/rapidjson/rapidjson/stream.h | 223 ++ deps/rapidjson/rapidjson/stringbuffer.h | 121 + deps/rapidjson/rapidjson/uri.h | 481 +++ deps/rapidjson/rapidjson/writer.h | 721 ++++ deps/rapidjson/version.txt | 5 + src/netlist/CMakeLists.txt | 2 +- src/utilities/CMakeLists.txt | 2 +- 42 files changed, 19088 insertions(+), 20 deletions(-) create mode 100644 deps/rapidjson/rapidjson/allocators.h create mode 100644 deps/rapidjson/rapidjson/cursorstreamwrapper.h create mode 100644 deps/rapidjson/rapidjson/document.h create mode 100644 deps/rapidjson/rapidjson/encodedstream.h create mode 100644 deps/rapidjson/rapidjson/encodings.h create mode 100644 deps/rapidjson/rapidjson/error/en.h create mode 100644 deps/rapidjson/rapidjson/error/error.h create mode 100644 deps/rapidjson/rapidjson/filereadstream.h create mode 100644 deps/rapidjson/rapidjson/filewritestream.h create mode 100644 deps/rapidjson/rapidjson/fwd.h create mode 100644 deps/rapidjson/rapidjson/internal/biginteger.h create mode 100644 deps/rapidjson/rapidjson/internal/clzll.h create mode 100644 deps/rapidjson/rapidjson/internal/diyfp.h create mode 100644 deps/rapidjson/rapidjson/internal/dtoa.h create mode 100644 deps/rapidjson/rapidjson/internal/ieee754.h create mode 100644 deps/rapidjson/rapidjson/internal/itoa.h create mode 100644 deps/rapidjson/rapidjson/internal/meta.h create mode 100644 deps/rapidjson/rapidjson/internal/pow10.h create mode 100644 deps/rapidjson/rapidjson/internal/regex.h create mode 100644 deps/rapidjson/rapidjson/internal/stack.h create mode 100644 deps/rapidjson/rapidjson/internal/strfunc.h create mode 100644 deps/rapidjson/rapidjson/internal/strtod.h create mode 100644 deps/rapidjson/rapidjson/internal/swap.h create mode 100644 deps/rapidjson/rapidjson/istreamwrapper.h create mode 100644 deps/rapidjson/rapidjson/memorybuffer.h create mode 100644 deps/rapidjson/rapidjson/memorystream.h create mode 100644 deps/rapidjson/rapidjson/msinttypes/inttypes.h create mode 100644 deps/rapidjson/rapidjson/msinttypes/stdint.h create mode 100644 deps/rapidjson/rapidjson/ostreamwrapper.h create mode 100644 deps/rapidjson/rapidjson/pointer.h create mode 100644 deps/rapidjson/rapidjson/prettywriter.h create mode 100644 deps/rapidjson/rapidjson/rapidjson.h create mode 100644 deps/rapidjson/rapidjson/reader.h create mode 100644 deps/rapidjson/rapidjson/schema.h create mode 100644 deps/rapidjson/rapidjson/stream.h create mode 100644 deps/rapidjson/rapidjson/stringbuffer.h create mode 100644 deps/rapidjson/rapidjson/uri.h create mode 100644 deps/rapidjson/rapidjson/writer.h create mode 100644 deps/rapidjson/version.txt diff --git a/cmake/detect_dependencies.cmake b/cmake/detect_dependencies.cmake index 6ee82c749e4..57a05cbf9c0 100644 --- a/cmake/detect_dependencies.cmake +++ b/cmake/detect_dependencies.cmake @@ -27,11 +27,22 @@ find_package(Sanitizers REQUIRED) # ############################### # #### Bitwuzla # ############################### -find_package(Bitwuzla) +pkg_check_modules(BITWUZLA bitwuzla) + +# find_package(Bitwuzla) + +if(BITWUZLA_FOUND) + message(STATUS "Found BITWUZLA") + message(STATUS " BITWUZLA_LIBRARIES: ${BITWUZLA_LIBRARIES}") + message(STATUS " BITWUZLA_LINK_LIBRARIES: ${BITWUZLA_LINK_LIBRARIES}") + message(STATUS " BITWUZLA_INCLUDE_DIRS: ${BITWUZLA_INCLUDE_DIRS}") +else() + set(BITWUZLA_LIBRARY "") + set(BITWUZLA_INCLUDE_DIRS "") + + message(STATUS "Bitwuzla not found, but this is optional...") +endif(BITWUZLA_FOUND) -if(Bitwuzla_FOUND) - set(BITWUZLA_LIBRARY Bitwuzla::bitwuzla) -endif() # ############################### # #### OpenMP @@ -109,23 +120,19 @@ endif() # ############################### find_package(Filesystem REQUIRED Final Experimental) + # ############################### # #### RapidJSON # ############################### -find_package(RapidJSON REQUIRED) -message(STATUS "Found rapidjson ${RAPIDJSON_INCLUDEDIR}") +message(STATUS "using rapidjson from deps") +add_library(RapidJSON::RapidJSON INTERFACE IMPORTED) +set_target_properties(RapidJSON::RapidJSON PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/deps/rapidjson" +) +set(RAPIDJSON_INCLUDEDIR "${CMAKE_SOURCE_DIR}/deps/rapidjson") +message(STATUS "Set rapidjson successully: ${RAPIDJSON_INCLUDEDIR}") -if(RapidJSON_FOUND AND NOT TARGET RapidJSON::RapidJSON) - if(NOT RAPIDJSON_INCLUDEDIR) - set(RAPIDJSON_INCLUDEDIR ${RAPIDJSON_INCLUDE_DIRS}) - endif() - add_library(RapidJSON::RapidJSON INTERFACE IMPORTED) - set_target_properties(RapidJSON::RapidJSON PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${RAPIDJSON_INCLUDEDIR}" - ) - message(STATUS "Set rapidjson successully: ${RAPIDJSON_INCLUDEDIR}") -endif() # ############################### # #### pybind11 @@ -243,7 +250,7 @@ endif(Z3_FOUND) # ############################### # #### igraph # ############################### -set (IGRAPH_SUBDIR "${CMAKE_SOURCE_DIR}/deps/igraph-0.9.10") +set(IGRAPH_SUBDIR "${CMAKE_SOURCE_DIR}/deps/igraph-0.9.10") add_subdirectory(${IGRAPH_SUBDIR}) get_directory_property(IGRAPH_INCLUDES DIRECTORY ${IGRAPH_SUBDIR} DEFINITION IGRAPH_INCLUDES) -get_directory_property(IGRAPH_LIB DIRECTORY ${IGRAPH_SUBDIR} DEFINITION IGRAPH_LIB) +get_directory_property(IGRAPH_LIB DIRECTORY ${IGRAPH_SUBDIR} DEFINITION IGRAPH_LIB) diff --git a/deps/rapidjson/rapidjson/allocators.h b/deps/rapidjson/rapidjson/allocators.h new file mode 100644 index 00000000000..275417bd8b3 --- /dev/null +++ b/deps/rapidjson/rapidjson/allocators.h @@ -0,0 +1,693 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" +#include "internal/meta.h" + +#include +#include + +#if RAPIDJSON_HAS_CXX11 +#include +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return RAPIDJSON_MALLOC(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + RAPIDJSON_FREE(originalPtr); + return NULL; + } + return RAPIDJSON_REALLOC(originalPtr, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); } + + bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return true; + } + bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return false; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + struct SharedData { + ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + BaseAllocator* ownBaseAllocator; //!< base allocator created by this object. + size_t refcount; + bool ownBuffer; + }; + + static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData)); + static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader)); + + static inline ChunkHeader *GetChunkHead(SharedData *shared) + { + return reinterpret_cast(reinterpret_cast(shared) + SIZEOF_SHARED_DATA); + } + static inline uint8_t *GetChunkBuffer(SharedData *shared) + { + return reinterpret_cast(shared->chunkHead) + SIZEOF_CHUNK_HEADER; + } + + static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. + +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + explicit + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()), + shared_(static_cast(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0)) + { + RAPIDJSON_ASSERT(baseAllocator_ != 0); + RAPIDJSON_ASSERT(shared_ != 0); + if (baseAllocator) { + shared_->ownBaseAllocator = 0; + } + else { + shared_->ownBaseAllocator = baseAllocator_; + } + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = 0; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBuffer = true; + shared_->refcount = 1; + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator), + shared_(static_cast(AlignBuffer(buffer, size))) + { + RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER); + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBaseAllocator = 0; + shared_->ownBuffer = false; + shared_->refcount = 1; + } + + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + ++shared_->refcount; + } + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + ++rhs.shared_->refcount; + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + rhs.shared_ = 0; + } + MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + rhs.shared_ = 0; + return *this; + } +#endif + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT { + if (!shared_) { + // do nothing if moved + return; + } + if (shared_->refcount > 1) { + --shared_->refcount; + return; + } + Clear(); + BaseAllocator *a = shared_->ownBaseAllocator; + if (shared_->ownBuffer) { + baseAllocator_->Free(shared_); + } + RAPIDJSON_DELETE(a); + } + + //! Deallocates all memory chunks, excluding the first/user one. + void Clear() RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + for (;;) { + ChunkHeader* c = shared_->chunkHead; + if (!c->next) { + break; + } + shared_->chunkHead = c->next; + baseAllocator_->Free(c); + } + shared_->chunkHead->size = 0; + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + size_t capacity = 0; + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + size_t size = 0; + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Whether the allocator is shared. + /*! \return true or false. + */ + bool Shared() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + return shared_->refcount > 1; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity)) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size; + shared_->chunkHead->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) { + shared_->chunkHead->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing + + //! Compare (equality) with another MemoryPoolAllocator + bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + return shared_ == rhs.shared_; + } + //! Compare (inequality) with another MemoryPoolAllocator + bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + return !operator==(rhs); + } + +private: + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = static_cast(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = shared_->chunkHead; + shared_->chunkHead = chunk; + return true; + } + else + return false; + } + + static inline void* AlignBuffer(void* buf, size_t &size) + { + RAPIDJSON_NOEXCEPT_ASSERT(buf != 0); + const uintptr_t mask = sizeof(void*) - 1; + const uintptr_t ubuf = reinterpret_cast(buf); + if (RAPIDJSON_UNLIKELY(ubuf & mask)) { + const uintptr_t abuf = (ubuf + mask) & ~mask; + RAPIDJSON_ASSERT(size >= abuf - ubuf); + buf = reinterpret_cast(abuf); + size -= abuf - ubuf; + } + return buf; + } + + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + SharedData *shared_; //!< The shared data of the allocator +}; + +namespace internal { + template + struct IsRefCounted : + public FalseType + { }; + template + struct IsRefCounted::Type> : + public TrueType + { }; +} + +template +inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n) +{ + RAPIDJSON_NOEXCEPT_ASSERT(old_n <= (std::numeric_limits::max)() / sizeof(T) && new_n <= (std::numeric_limits::max)() / sizeof(T)); + return static_cast(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T))); +} + +template +inline T *Malloc(A& a, size_t n = 1) +{ + return Realloc(a, NULL, 0, n); +} + +template +inline void Free(A& a, T *p, size_t n = 1) +{ + static_cast(Realloc(a, p, n, 0)); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited +#endif + +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; +#if RAPIDJSON_HAS_CXX11 + typedef std::allocator_traits traits_type; +#else + typedef allocator_type traits_type; +#endif + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(std::move(rhs)), + baseAllocator_(std::move(rhs.baseAllocator_)) + { } +#endif +#if RAPIDJSON_HAS_CXX11 + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; +#endif + + /* implicit */ + StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(baseAllocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; + }; + + typedef typename traits_type::size_type size_type; + typedef typename traits_type::difference_type difference_type; + + typedef typename traits_type::value_type value_type; + typedef typename traits_type::pointer pointer; + typedef typename traits_type::const_pointer const_pointer; + +#if RAPIDJSON_HAS_CXX11 + + typedef typename std::add_lvalue_reference::type &reference; + typedef typename std::add_lvalue_reference::type>::type &const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return traits_type::max_size(*this); + } + + template + void construct(pointer p, Args&&... args) + { + traits_type::construct(*this, p, std::forward(args)...); + } + void destroy(pointer p) + { + traits_type::destroy(*this, p); + } + +#else // !RAPIDJSON_HAS_CXX11 + + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return allocator_type::max_size(); + } + + void construct(pointer p, const_reference r) + { + allocator_type::construct(p, r); + } + void destroy(pointer p) + { + allocator_type::destroy(p); + } + +#endif // !RAPIDJSON_HAS_CXX11 + + template + U* allocate(size_type n = 1, const void* = 0) + { + return RAPIDJSON_NAMESPACE::Malloc(baseAllocator_, n); + } + template + void deallocate(U* p, size_type n = 1) + { + RAPIDJSON_NAMESPACE::Free(baseAllocator_, p, n); + } + + pointer allocate(size_type n = 1, const void* = 0) + { + return allocate(n); + } + void deallocate(pointer p, size_type n = 1) + { + deallocate(p, n); + } + +#if RAPIDJSON_HAS_CXX11 + using is_always_equal = std::is_empty; +#endif + + template + bool operator==(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return baseAllocator_ == rhs.baseAllocator_; + } + template + bool operator!=(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return !operator==(rhs); + } + + //! rapidjson Allocator concept + static const bool kNeedFree = BaseAllocator::kNeedFree; + static const bool kRefCounted = internal::IsRefCounted::Value; + void* Malloc(size_t size) + { + return baseAllocator_.Malloc(size); + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) + { + return baseAllocator_.Realloc(originalPtr, originalSize, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT + { + BaseAllocator::Free(ptr); + } + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; +}; + +#if !RAPIDJSON_HAS_CXX17 // std::allocator deprecated in C++17 +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + /* implicit */ + StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(baseAllocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; + }; + + typedef typename allocator_type::value_type value_type; + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; +}; +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/deps/rapidjson/rapidjson/cursorstreamwrapper.h b/deps/rapidjson/rapidjson/cursorstreamwrapper.h new file mode 100644 index 00000000000..fd6513db14a --- /dev/null +++ b/deps/rapidjson/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/deps/rapidjson/rapidjson/document.h b/deps/rapidjson/rapidjson/document.h new file mode 100644 index 00000000000..2cd9a70a600 --- /dev/null +++ b/deps/rapidjson/rapidjson/document.h @@ -0,0 +1,3043 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include +#ifdef __cpp_lib_three_way_comparison +#include +#endif + +RAPIDJSON_DIAG_PUSH +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#endif // __GNUC__ + +#ifdef GetObject +// see https://github.com/Tencent/rapidjson/issues/1448 +// a former included windows.h might have defined a macro called GetObject, which affects +// GetObject defined here. This ensures the macro does not get applied +#pragma push_macro("GetObject") +#define RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#undef GetObject +#endif + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::random_access_iterator_tag +#endif + +#if RAPIDJSON_USE_MEMBERSMAP +#include // std::multimap +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +/*! \def RAPIDJSON_DEFAULT_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default allocator. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_ALLOCATOR +#define RAPIDJSON_DEFAULT_ALLOCATOR ::RAPIDJSON_NAMESPACE::MemoryPoolAllocator<::RAPIDJSON_NAMESPACE::CrtAllocator> +#endif + +/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default stack allocator for Document. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR +#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR ::RAPIDJSON_NAMESPACE::CrtAllocator +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultObjectCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY +// number of objects that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultArrayCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY +// number of array elements that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 +#endif + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +class GenericMember { +public: + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT + : name(std::move(rhs.name)), + value(std::move(rhs.value)) + { + } + + //! Move assignment in C++11 + GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { + return *this = static_cast(rhs); + } +#endif + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. + */ + GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + name = rhs.name; + value = rhs.value; + } + return *this; + } + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } + +private: + //! Copy constructor is not permitted. + GenericMember(const GenericMember& rhs); +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + + //! Pointer to (const) GenericMember + typedef pointer Pointer; + //! Reference to (const) GenericMember + typedef reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } + template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } + template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } + template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } + template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } + template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } + +#ifdef __cpp_lib_three_way_comparison + template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } +#endif + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +class GenericMemberIterator; + +//! non-const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(NotNullStrLen(str)) {} + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; +}; + +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + \see CopyFrom() + */ + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: + DoCopyMembers(rhs, allocator, copyConstStrings); + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + // With RAPIDJSON_USE_MEMBERSMAP, the maps need to be destroyed to release + // their Allocator if it's refcounted (e.g. MemoryPoolAllocator). + if (Allocator::kNeedFree || (RAPIDJSON_USE_MEMBERSMAP+0 && + internal::IsRefCounted::Value)) { + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(e); + } + } + break; + + case kObjectFlag: + DoFreeMembers(); + break; + + case kCopyStringFlag: + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(const_cast(GetStringPointer())); + } + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + // Can't destroy "this" before assigning "rhs", otherwise "rhs" + // could be used after free if it's an sub-Value of "this", + // hence the temporary danse. + GenericValue temp; + temp.RawAssign(rhs); + this->~GenericValue(); + RawAssign(temp); + } + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator, copyConstStrings); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + +#ifndef __cpp_impl_three_way_comparison + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} +#endif + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast((std::numeric_limits::max)())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + +#if RAPIDJSON_HAS_CXX11 + // Use thread-local storage to prevent races between threads. + // Use static buffer and placement-new to prevent destruction, with + // alignas() to ensure proper alignment. + alignas(GenericValue) thread_local static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); +#elif defined(_MSC_VER) && _MSC_VER < 1900 + // There's no way to solve both thread locality and proper alignment + // simultaneously. + __declspec(thread) static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); +#elif defined(__GNUC__) || defined(__clang__) + // This will generate -Wexit-time-destructors in clang, but that's + // better than having under-alignment. + __thread static GenericValue buffer; + return buffer; +#else + // Don't know what compiler this is, so don't know how to ensure + // thread-locality. + static GenericValue buffer; + return buffer; +#endif + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + DoReserveMembers(newCapacity, allocator); + return *this; + } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + return DoFindMember(name); + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + DoAddMember(name, value, allocator); + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + DoClearMembers(); + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + return DoRemoveMember(m); + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + return DoEraseMembers(first, last); + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + Object GetObj() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + ConstObject GetObj() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return DataString(data_); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return DataStringLength(data_); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (ConstValueIterator v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. + kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), + kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), + kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), + kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), + kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), + kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), + kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), + kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), + kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), + kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), + kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; + static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + static RAPIDJSON_FORCEINLINE const Ch* DataString(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.str : RAPIDJSON_GETPOINTER(Ch, data.s.str); + } + static RAPIDJSON_FORCEINLINE SizeType DataStringLength(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.GetLength() : data.s.length; + } + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + +#if RAPIDJSON_USE_MEMBERSMAP + + struct MapTraits { + struct Less { + bool operator()(const Data& s1, const Data& s2) const { + SizeType n1 = DataStringLength(s1), n2 = DataStringLength(s2); + int cmp = std::memcmp(DataString(s1), DataString(s2), sizeof(Ch) * (n1 < n2 ? n1 : n2)); + return cmp < 0 || (cmp == 0 && n1 < n2); + } + }; + typedef std::pair Pair; + typedef std::multimap > Map; + typedef typename Map::iterator Iterator; + }; + typedef typename MapTraits::Map Map; + typedef typename MapTraits::Less MapLess; + typedef typename MapTraits::Pair MapPair; + typedef typename MapTraits::Iterator MapIterator; + + // + // Layout of the members' map/array, re(al)located according to the needed capacity: + // + // {Map*}<>{capacity}<>{Member[capacity]}<>{MapIterator[capacity]} + // + // (where <> stands for the RAPIDJSON_ALIGN-ment, if needed) + // + + static RAPIDJSON_FORCEINLINE size_t GetMapLayoutSize(SizeType capacity) { + return RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(capacity * sizeof(Member)) + + capacity * sizeof(MapIterator); + } + + static RAPIDJSON_FORCEINLINE SizeType &GetMapCapacity(Map* &map) { + return *reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + static RAPIDJSON_FORCEINLINE Member* GetMapMembers(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType))); + } + + static RAPIDJSON_FORCEINLINE MapIterator* GetMapIterators(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(GetMapCapacity(map) * sizeof(Member))); + } + + static RAPIDJSON_FORCEINLINE Map* &GetMap(Member* members) { + RAPIDJSON_ASSERT(members != 0); + return *reinterpret_cast(reinterpret_cast(members) - + RAPIDJSON_ALIGN(sizeof(SizeType)) - + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + // Some compilers' debug mechanisms want all iterators to be destroyed, for their accounting.. + RAPIDJSON_FORCEINLINE MapIterator DropMapIterator(MapIterator& rhs) { +#if RAPIDJSON_HAS_CXX11 + MapIterator ret = std::move(rhs); +#else + MapIterator ret = rhs; +#endif + rhs.~MapIterator(); + return ret; + } + + Map* &DoReallocMap(Map** oldMap, SizeType newCapacity, Allocator& allocator) { + Map **newMap = static_cast(allocator.Malloc(GetMapLayoutSize(newCapacity))); + GetMapCapacity(*newMap) = newCapacity; + if (!oldMap) { + *newMap = new (allocator.Malloc(sizeof(Map))) Map(MapLess(), allocator); + } + else { + *newMap = *oldMap; + size_t count = (*oldMap)->size(); + std::memcpy(static_cast(GetMapMembers(*newMap)), + static_cast(GetMapMembers(*oldMap)), + count * sizeof(Member)); + MapIterator *oldIt = GetMapIterators(*oldMap), + *newIt = GetMapIterators(*newMap); + while (count--) { + new (&newIt[count]) MapIterator(DropMapIterator(oldIt[count])); + } + Allocator::Free(oldMap); + } + return *newMap; + } + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return GetMapMembers(DoReallocMap(0, capacity, allocator)); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* oldMembers = GetMembersPointer(); + Map **oldMap = oldMembers ? &GetMap(oldMembers) : 0, + *&newMap = DoReallocMap(oldMap, newCapacity, allocator); + RAPIDJSON_SETPOINTER(Member, o.members, GetMapMembers(newMap)); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator mit = map->find(reinterpret_cast(name.data_)); + if (mit != map->end()) { + return MemberIterator(&members[mit->second]); + } + } + return MemberEnd(); + } + + void DoClearMembers() { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < data_.o.size; i++) { + map->erase(DropMapIterator(mit[i])); + members[i].~Member(); + } + data_.o.size = 0; + } + } + + void DoFreeMembers() { + if (Member* members = GetMembersPointer()) { + GetMap(members)->~Map(); + for (SizeType i = 0; i < data_.o.size; i++) { + members[i].~Member(); + } + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Map** map = &GetMap(members); + Allocator::Free(*map); + Allocator::Free(map); + } + } + } + +#else // !RAPIDJSON_USE_MEMBERSMAP + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return Malloc(allocator, capacity); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* newMembers = Realloc(allocator, GetMembersPointer(), o.capacity, newCapacity); + RAPIDJSON_SETPOINTER(Member, o.members, newMembers); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + + void DoClearMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + void DoFreeMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + } + +#endif // !RAPIDJSON_USE_MEMBERSMAP + + void DoAddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + ObjectData& o = data_.o; + if (o.size >= o.capacity) + DoReserveMembers(o.capacity ? (o.capacity + (o.capacity + 1) / 2) : kDefaultObjectCapacity, allocator); + Member* members = GetMembersPointer(); + Member* m = members + o.size; + m->name.RawAssign(name); + m->value.RawAssign(value); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + new (&mit[o.size]) MapIterator(map->insert(MapPair(m->name.data_, o.size))); +#endif + ++o.size; + } + + MemberIterator DoRemoveMember(MemberIterator m) { + ObjectData& o = data_.o; + Member* members = GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + SizeType mpos = static_cast(&*m - members); + map->erase(DropMapIterator(mit[mpos])); +#endif + MemberIterator last(members + (o.size - 1)); + if (o.size > 1 && m != last) { +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[mpos]) MapIterator(DropMapIterator(mit[&*last - members])); + mit[mpos]->second = mpos; +#endif + *m = *last; // Move the last one to this place + } + else { + m->~Member(); // Only one left, just destroy + } + --o.size; + return m; + } + + MemberIterator DoEraseMembers(ConstMemberIterator first, ConstMemberIterator last) { + ObjectData& o = data_.o; + MemberIterator beg = MemberBegin(), + pos = beg + (first - beg), + end = MemberEnd(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(GetMembersPointer()); + MapIterator* mit = GetMapIterators(map); +#endif + for (MemberIterator itr = pos; itr != last; ++itr) { +#if RAPIDJSON_USE_MEMBERSMAP + map->erase(DropMapIterator(mit[itr - beg])); +#endif + itr->~Member(); + } +#if RAPIDJSON_USE_MEMBERSMAP + if (first != last) { + // Move remaining members/iterators + MemberIterator next = pos + (last - first); + for (MemberIterator itr = pos; next != end; ++itr, ++next) { + std::memcpy(static_cast(&*itr), &*next, sizeof(Member)); + SizeType mpos = static_cast(itr - beg); + new (&mit[mpos]) MapIterator(DropMapIterator(mit[next - beg])); + mit[mpos]->second = mpos; + } + } +#else + std::memmove(static_cast(&*pos), &*last, + static_cast(end - last) * sizeof(Member)); +#endif + o.size -= static_cast(last - first); + return pos; + } + + template + void DoCopyMembers(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings) { + RAPIDJSON_ASSERT(rhs.GetType() == kObjectType); + + data_.f.flags = kObjectFlag; + SizeType count = rhs.data_.o.size; + Member* lm = DoAllocMembers(count, allocator); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(lm); + MapIterator* mit = GetMapIterators(map); +#endif + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[i]) MapIterator(map->insert(MapPair(lm[i].name.data_, i))); +#endif + } + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = DoAllocMembers(count, allocator); + SetMembersPointer(m); + std::memcpy(static_cast(m), members, count * sizeof(Member)); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(m); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < count; i++) { + new (&mit[i]) MapIterator(map->insert(MapPair(m[i].name.data_, i))); + } +#endif + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef StackAllocator StackAllocatorType; //!< StackAllocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + // Clear the ::ValueType before ownAllocator is destroyed, ~ValueType() + // runs last and may access its elements or members which would be freed + // with an allocator like MemoryPoolAllocator (CrtAllocator does not + // free its data when destroyed, but MemoryPoolAllocator does). + if (ownAllocator_) { + ValueType::SetNull(); + } + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + operator ValueType&() const { return value_; } + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + operator ValueType&() const { return value_; } + SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#ifdef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#pragma pop_macro("GetObject") +#undef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#endif + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/deps/rapidjson/rapidjson/encodedstream.h b/deps/rapidjson/rapidjson/encodedstream.h new file mode 100644 index 00000000000..cf046b89235 --- /dev/null +++ b/deps/rapidjson/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/deps/rapidjson/rapidjson/encodings.h b/deps/rapidjson/rapidjson/encodings.h new file mode 100644 index 00000000000..50ad18bdc08 --- /dev/null +++ b/deps/rapidjson/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFFu >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define RAPIDJSON_COPY() os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + Ch c; + RAPIDJSON_COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/deps/rapidjson/rapidjson/error/en.h b/deps/rapidjson/rapidjson/error/en.h new file mode 100644 index 00000000000..c87b04eb133 --- /dev/null +++ b/deps/rapidjson/rapidjson/error/en.h @@ -0,0 +1,176 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of validation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param validateErrorCode Error code obtained from validator. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred"); + case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'."); + case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'."); + case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'."); + case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'."); + case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'."); + + case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'."); + case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'."); + case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression."); + + case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'."); + case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'."); + case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true."); + case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema."); + + case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'."); + case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'."); + case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'."); + case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema."); + case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema."); + case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors."); + + case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values."); + case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'."); + + case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors."); + case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf', indices '%matches'."); + case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors."); + case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors."); + case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'."); + + case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing."); + case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of schema document compilation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param schemaErrorCode Error code obtained from compiling the schema document. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ + inline const RAPIDJSON_ERROR_CHARTYPE* GetSchemaError_En(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kSchemaErrorStartUnknown: return RAPIDJSON_ERROR_STRING("Pointer '%value' to start of schema does not resolve to a location in the document."); + case kSchemaErrorRefPlainName: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' must be a JSON pointer."); + case kSchemaErrorRefInvalid: return RAPIDJSON_ERROR_STRING("$ref must not be an empty string."); + case kSchemaErrorRefPointerInvalid: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' is not a valid JSON pointer at offset '%offset'."); + case kSchemaErrorRefUnknown: return RAPIDJSON_ERROR_STRING("$ref '%value' does not resolve to a location in the target document."); + case kSchemaErrorRefCyclical: return RAPIDJSON_ERROR_STRING("$ref '%value' is cyclical."); + case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider."); + case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema."); + case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'."); + case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized."); + case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported."); + case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document."); + case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } + } + +//! Maps error code of pointer parse into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param pointerParseErrorCode Error code obtained from pointer parse. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetPointerParseError_En(PointerParseErrorCode pointerParseErrorCode) { + switch (pointerParseErrorCode) { + case kPointerParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kPointerParseErrorTokenMustBeginWithSolidus: return RAPIDJSON_ERROR_STRING("A token must begin with a '/'."); + case kPointerParseErrorInvalidEscape: return RAPIDJSON_ERROR_STRING("Invalid escape."); + case kPointerParseErrorInvalidPercentEncoding: return RAPIDJSON_ERROR_STRING("Invalid percent encoding in URI fragment."); + case kPointerParseErrorCharacterMustPercentEncode: return RAPIDJSON_ERROR_STRING("A character must be percent encoded in a URI fragment."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/deps/rapidjson/rapidjson/error/error.h b/deps/rapidjson/rapidjson/error/error.h new file mode 100644 index 00000000000..cae345db36d --- /dev/null +++ b/deps/rapidjson/rapidjson/error/error.h @@ -0,0 +1,285 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literal to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// ValidateErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum ValidateErrorCode { + kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set. + kValidateErrorNone = 0, //!< No error. + + kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value. + kValidateErrorMaximum, //!< Number is greater than the 'maximum' value. + kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value. + kValidateErrorMinimum, //!< Number is less than the 'minimum' value. + kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value. + + kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value. + kValidateErrorMinLength, //!< String is longer than the 'maxLength' value. + kValidateErrorPattern, //!< String does not match the 'pattern' regular expression. + + kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value. + kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value. + kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true. + kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema. + + kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value. + kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value. + kValidateErrorRequired, //!< Object is missing one or more members required by the schema. + kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema. + kValidateErrorPatternProperties, //!< See other errors. + kValidateErrorDependencies, //!< Object has missing property or schema dependencies. + + kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values. + kValidateErrorType, //!< Property has a type that is not allowed by the schema. + + kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'. + kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'. + kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'. + kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'. + kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'. + + kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing + kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading +}; + +//! Function pointer type of GetValidateError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetValidateError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// SchemaErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum SchemaErrorCode { + kSchemaErrorNone = 0, //!< No error. + + kSchemaErrorStartUnknown, //!< Pointer to start of schema does not resolve to a location in the document + kSchemaErrorRefPlainName, //!< $ref fragment must be a JSON pointer + kSchemaErrorRefInvalid, //!< $ref must not be an empty string + kSchemaErrorRefPointerInvalid, //!< $ref fragment is not a valid JSON pointer at offset + kSchemaErrorRefUnknown, //!< $ref does not resolve to a location in the target document + kSchemaErrorRefCyclical, //!< $ref is cyclical + kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider + kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema + kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties' + kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized + kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported + kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document + kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly' +}; + +//! Function pointer type of GetSchemaError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetSchemaError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetSchemaErrorFunc GetSchemaError = GetSchemaError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetSchemaError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetSchemaErrorFunc)(SchemaErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// PointerParseErrorCode + +//! Error code of JSON pointer parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +//! Function pointer type of GetPointerParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetPointerParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetPointerParseErrorFunc GetPointerParseError = GetPointerParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetPointerParseError(pointer.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetPointerParseErrorFunc)(PointerParseErrorCode); + + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/deps/rapidjson/rapidjson/filereadstream.h b/deps/rapidjson/rapidjson/filereadstream.h new file mode 100644 index 00000000000..f8bb43cb0cf --- /dev/null +++ b/deps/rapidjson/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/deps/rapidjson/rapidjson/filewritestream.h b/deps/rapidjson/rapidjson/filewritestream.h new file mode 100644 index 00000000000..5d89588c218 --- /dev/null +++ b/deps/rapidjson/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for output using fwrite(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/deps/rapidjson/rapidjson/fwd.h b/deps/rapidjson/rapidjson/fwd.h new file mode 100644 index 00000000000..d62f77f0ecf --- /dev/null +++ b/deps/rapidjson/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +class GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/deps/rapidjson/rapidjson/internal/biginteger.h b/deps/rapidjson/rapidjson/internal/biginteger.h new file mode 100644 index 00000000000..4930043dc7c --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/biginteger.h @@ -0,0 +1,297 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64) +#include // for _umul128 +#if !defined(_ARM64EC_) +#pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + template + BigInteger(const Ch* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + template + void AppendDecimal64(const Ch* begin, const Ch* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + template + static uint64_t ParseUint64(const Ch* begin, const Ch* end) { + uint64_t r = 0; + for (const Ch* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9')); + r = r * 10u + static_cast(*p - Ch('0')); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/deps/rapidjson/rapidjson/internal/clzll.h b/deps/rapidjson/rapidjson/internal/clzll.h new file mode 100644 index 00000000000..8fc5118aa47 --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/clzll.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CLZLL_H_ +#define RAPIDJSON_CLZLL_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(UNDER_CE) +#include +#if defined(_WIN64) +#pragma intrinsic(_BitScanReverse64) +#else +#pragma intrinsic(_BitScanReverse) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline uint32_t clzll(uint64_t x) { + // Passing 0 to __builtin_clzll is UB in GCC and results in an + // infinite loop in the software implementation. + RAPIDJSON_ASSERT(x != 0); + +#if defined(_MSC_VER) && !defined(UNDER_CE) + unsigned long r = 0; +#if defined(_WIN64) + _BitScanReverse64(&r, x); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); +#endif // _WIN64 + + return 63 - r; +#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) + // __builtin_clzll wrapper + return static_cast(__builtin_clzll(x)); +#else + // naive version + uint32_t r = 0; + while (!(x & (static_cast(1) << 63))) { + x <<= 1; + ++r; + } + + return r; +#endif // _MSC_VER +} + +#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CLZLL_H_ diff --git a/deps/rapidjson/rapidjson/internal/diyfp.h b/deps/rapidjson/rapidjson/internal/diyfp.h new file mode 100644 index 00000000000..1f60fb60ca0 --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/diyfp.h @@ -0,0 +1,261 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" +#include "clzll.h" +#include + +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) +#include +#if !defined(_ARM64EC_) +#pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() : f(), e() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { + int s = static_cast(clzll(f)); + return DiyFp(f << s, e - s); + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + RAPIDJSON_ASSERT(index < 87); + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/deps/rapidjson/rapidjson/internal/dtoa.h b/deps/rapidjson/rapidjson/internal/dtoa.h new file mode 100644 index 00000000000..cd456721a71 --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/dtoa.h @@ -0,0 +1,249 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline int CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL, + 1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL, + 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, + 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, + 10000000000000000000ULL }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0)); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/deps/rapidjson/rapidjson/internal/ieee754.h b/deps/rapidjson/rapidjson/internal/ieee754.h new file mode 100644 index 00000000000..68c9e96649b --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static int EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return order + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/deps/rapidjson/rapidjson/internal/itoa.h b/deps/rapidjson/rapidjson/internal/itoa.h new file mode 100644 index 00000000000..9fe8c932ffa --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/itoa.h @@ -0,0 +1,308 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/deps/rapidjson/rapidjson/internal/meta.h b/deps/rapidjson/rapidjson/internal/meta.h new file mode 100644 index 00000000000..27092dc0d69 --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/meta.h @@ -0,0 +1,186 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/deps/rapidjson/rapidjson/internal/pow10.h b/deps/rapidjson/rapidjson/internal/pow10.h new file mode 100644 index 00000000000..eae1a43ed1a --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/deps/rapidjson/rapidjson/internal/regex.h b/deps/rapidjson/rapidjson/internal/regex.h new file mode 100644 index 00000000000..6446c403af9 --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/regex.h @@ -0,0 +1,739 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +class DecodedStream { +public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + +private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +template +class GenericRegexSearch; + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef Encoding EncodingType; + typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream, Encoding> ds(ss); + Parse(ds); + } + + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Stack operandStack(allocator_, 256); // Frag + Stack operatorStack(allocator_, 256); // Operator + Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + case kOneOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + + default: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + Allocator* ownAllocator_; + Allocator* allocator_; + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + bool anchorBegin_; + bool anchorEnd_; +}; + +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + +typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/deps/rapidjson/rapidjson/internal/stack.h b/deps/rapidjson/rapidjson/internal/stack.h new file mode 100644 index 00000000000..73abd706e97 --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/stack.h @@ -0,0 +1,232 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" +#include + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/deps/rapidjson/rapidjson/internal/strfunc.h b/deps/rapidjson/rapidjson/internal/strfunc.h new file mode 100644 index 00000000000..b698a8f43fa --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/strfunc.h @@ -0,0 +1,83 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Custom strcmpn() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s1 Null-terminated input string. + \param s2 Null-terminated input string. + \return 0 if equal +*/ +template +inline int StrCmp(const Ch* s1, const Ch* s2) { + RAPIDJSON_ASSERT(s1 != 0); + RAPIDJSON_ASSERT(s2 != 0); + while(*s1 && (*s1 == *s2)) { s1++; s2++; } + return static_cast(*s1) < static_cast(*s2) ? -1 : static_cast(*s1) > static_cast(*s2); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/deps/rapidjson/rapidjson/internal/strtod.h b/deps/rapidjson/rapidjson/internal/strtod.h new file mode 100644 index 00000000000..55f0e380bfa --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/strtod.h @@ -0,0 +1,293 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" +#include +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +template +inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) { + uint64_t significand = 0; + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5'))) + break; + significand = significand * 10u + static_cast(decimals[i] - Ch('0')); + } + + if (i < dLen && decimals[i] >= Ch('5')) // Rounding + significand++; + + int remaining = dLen - i; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + dExp += remaining; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + int scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +template +inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +template +inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result = 0.0; + if (StrtodFast(d, p, &result)) + return result; + + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + + // Trim leading zeros + while (dLen > 0 && *decimals == '0') { + dLen--; + decimals++; + } + + // Trim trailing zeros + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; + } + + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) + return 0.0; + + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, dLen, dExp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/deps/rapidjson/rapidjson/internal/swap.h b/deps/rapidjson/rapidjson/internal/swap.h new file mode 100644 index 00000000000..2cf92f93a1d --- /dev/null +++ b/deps/rapidjson/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/deps/rapidjson/rapidjson/istreamwrapper.h b/deps/rapidjson/rapidjson/istreamwrapper.h new file mode 100644 index 00000000000..01437ec0127 --- /dev/null +++ b/deps/rapidjson/rapidjson/istreamwrapper.h @@ -0,0 +1,128 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); + } + + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + BasicIStreamWrapper(); + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/deps/rapidjson/rapidjson/memorybuffer.h b/deps/rapidjson/rapidjson/memorybuffer.h new file mode 100644 index 00000000000..ffbc41ed1f7 --- /dev/null +++ b/deps/rapidjson/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/deps/rapidjson/rapidjson/memorystream.h b/deps/rapidjson/rapidjson/memorystream.h new file mode 100644 index 00000000000..77af6c999e9 --- /dev/null +++ b/deps/rapidjson/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/deps/rapidjson/rapidjson/msinttypes/inttypes.h b/deps/rapidjson/rapidjson/msinttypes/inttypes.h new file mode 100644 index 00000000000..18111286bf5 --- /dev/null +++ b/deps/rapidjson/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/deps/rapidjson/rapidjson/msinttypes/stdint.h b/deps/rapidjson/rapidjson/msinttypes/stdint.h new file mode 100644 index 00000000000..3d4477b9a02 --- /dev/null +++ b/deps/rapidjson/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/deps/rapidjson/rapidjson/ostreamwrapper.h b/deps/rapidjson/rapidjson/ostreamwrapper.h new file mode 100644 index 00000000000..11ed4d33f92 --- /dev/null +++ b/deps/rapidjson/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/deps/rapidjson/rapidjson/pointer.h b/deps/rapidjson/rapidjson/pointer.h new file mode 100644 index 00000000000..6f4ef389268 --- /dev/null +++ b/deps/rapidjson/rapidjson/pointer.h @@ -0,0 +1,1476 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "uri.h" +#include "internal/itoa.h" +#include "error/error.h" // PointerParseErrorCode + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + typedef GenericUri UriType; + + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, internal::StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = static_cast(buffer[i]); + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + m = v->MemberEnd(); + v = &(--m)->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Compute URI + //@{ + + //! Compute the in-scope URI for a subtree. + // For use with JSON pointers into JSON schema documents. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param rootUri Root URI + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \param allocator Allocator for Uris + \return Uri if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a URI cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + static const Ch kIdString[] = { 'i', 'd', '\0' }; + static const ValueType kIdValue(kIdString, 2); + UriType base = UriType(rootUri, allocator); + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + // See if we have an id, and if so resolve with the current base + typename ValueType::MemberIterator m = v->FindMember(kIdValue); + if (m != v->MemberEnd() && (m->value).IsString()) { + UriType here = UriType(m->value, allocator).Resolve(base, allocator); + base = here; + } + m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return UriType(allocator); + } + return base; + } + + UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + return GetUri(const_cast(root), rootUri, unresolvedTokenIndex, allocator); + } + + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // The names of each token point to a string in the nameBuffer_. The + // previous memcpy copied over string pointers into the rhs.nameBuffer_, + // but they should point to the strings in the new nameBuffer_. + for (size_t i = 0; i < rhs.tokenCount_; ++i) { + // The offset between the string address and the name buffer should + // still be constant, so we can just get this offset and set each new + // token name according the new buffer start + the known offset. + std::ptrdiff_t name_offset = rhs.tokens_[i].name - rhs.nameBuffer_; + tokens_[i].name = nameBuffer_ + name_offset; + } + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/deps/rapidjson/rapidjson/prettywriter.h b/deps/rapidjson/rapidjson/prettywriter.h new file mode 100644 index 00000000000..fe45df1d10f --- /dev/null +++ b/deps/rapidjson/rapidjson/prettywriter.h @@ -0,0 +1,277 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of output os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::EndValue(Base::WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kStringType); + return Base::EndValue(Base::WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndObject()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndArray()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::EndValue(Base::WriteRawValue(json, length)); + } + +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/deps/rapidjson/rapidjson/rapidjson.h b/deps/rapidjson/rapidjson/rapidjson.h new file mode 100644 index 00000000000..5ea69479501 --- /dev/null +++ b/deps/rapidjson/rapidjson/rapidjson.h @@ -0,0 +1,741 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overridden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// __cplusplus macro + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#if defined(_MSC_VER) +#define RAPIDJSON_CPLUSPLUS _MSVC_LANG +#else +#define RAPIDJSON_CPLUSPLUS __cplusplus +#endif + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_USE_MEMBERSMAP + +/*! \def RAPIDJSON_USE_MEMBERSMAP + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for object members handling in a \c std::multimap + + By defining this preprocessor symbol to \c 1, \ref rapidjson::GenericValue object + members are stored in a \c std::multimap for faster lookup and deletion times, a + trade off with a slightly slower insertion time and a small object allocat(or)ed + memory overhead. + + \hideinitializer +*/ +#ifndef RAPIDJSON_USE_MEMBERSMAP +#define RAPIDJSON_USE_MEMBERSMAP 0 // not by default +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. The default is 8 bytes. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2/Neon optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. + + To enable these optimizations, three different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if RAPIDJSON_CPLUSPLUS >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif // RAPIDJSON_STATIC_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +#if defined(__has_builtin) +#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) +#else +#define RAPIDJSON_HAS_BUILTIN(x) 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L) +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#elif defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#elif defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#ifndef RAPIDJSON_NOEXCEPT +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT throw() +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#endif + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +/////////////////////////////////////////////////////////////////////////////// +// C++17 features + +#ifndef RAPIDJSON_HAS_CXX17 +#define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L) +#endif + +#if RAPIDJSON_HAS_CXX17 +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] +#elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(clang::fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]] +# elif __has_cpp_attribute(fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough)) +# else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +# endif +#else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +#endif + +//!@endcond + +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#include +#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// malloc/realloc/free + +#ifndef RAPIDJSON_MALLOC +///! customization point for global \c malloc +#define RAPIDJSON_MALLOC(size) std::malloc(size) +#endif +#ifndef RAPIDJSON_REALLOC +///! customization point for global \c realloc +#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size) +#endif +#ifndef RAPIDJSON_FREE +///! customization point for global \c free +#define RAPIDJSON_FREE(ptr) std::free(ptr) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(TypeName) new TypeName +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/deps/rapidjson/rapidjson/reader.h b/deps/rapidjson/rapidjson/reader.h new file mode 100644 index 00000000000..55546601e29 --- /dev/null +++ b/deps/rapidjson/rapidjson/reader.h @@ -0,0 +1,2246 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : + stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n') {} + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe + is.Take(); + os.Put('\''); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + // high surrogate, check if followed by valid low surrogate + if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + // single low surrogate + else + { + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + } + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + uint32_t lz = internal::clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const StackCharacter* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s), stackStream(reader.stack_) {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(StackCharacter c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const StackCharacter* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s) {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + typedef typename internal::SelectIf, typename TargetEncoding::Ch, char>::Type NumberCharacter; + + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + } + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + GenericStringStream > srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const NumberCharacter* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingMemberValueState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; + IterativeParsingState state_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/deps/rapidjson/rapidjson/schema.h b/deps/rapidjson/rapidjson/schema.h new file mode 100644 index 00000000000..973e935f128 --- /dev/null +++ b/deps/rapidjson/rapidjson/schema.h @@ -0,0 +1,3262 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include "stringbuffer.h" +#include "error/en.h" +#include "uri.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeywordData(const char* keyword) { + printf(" Fail keyword: '%s'\n", keyword); +} + +inline void PrintInvalidKeywordData(const wchar_t* keyword) { + wprintf(L" Fail keyword: '%ls'\n", keyword); +} + +inline void PrintInvalidDocumentData(const char* document) { + printf(" Fail document: '%s'\n", document); +} + +inline void PrintInvalidDocumentData(const wchar_t* document) { + wprintf(L" Fail document: '%ls'\n", document); +} + +inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) { + printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) { + wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) { + printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved); +} + +inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) { + wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved); +} + +inline void PrintMethodData(const char* method) { + printf("%s\n", method); +} + +inline void PrintMethodData(const char* method, bool b) { + printf("%s, Data: '%s'\n", method, b ? "true" : "false"); +} + +inline void PrintMethodData(const char* method, int64_t i) { + printf("%s, Data: '%" PRId64 "'\n", method, i); +} + +inline void PrintMethodData(const char* method, uint64_t u) { + printf("%s, Data: '%" PRIu64 "'\n", method, u); +} + +inline void PrintMethodData(const char* method, double d) { + printf("%s, Data: '%lf'\n", method, d); +} + +inline void PrintMethodData(const char* method, const char* s) { + printf("%s, Data: '%s'\n", method, s); +} + +inline void PrintMethodData(const char* method, const wchar_t* s) { + wprintf(L"%hs, Data: '%ls'\n", method, s); +} + +inline void PrintMethodData(const char* method, const char* s1, const char* s2) { + printf("%s, Data: '%s', '%s'\n", method, s1, s2); +} + +inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) { + wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +#ifndef RAPIDJSON_SCHEMA_PRINT +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__) +#else +#define RAPIDJSON_SCHEMA_PRINT(name, ...) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidCode = code;\ + context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\ + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// ValidateFlag + +/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kValidateDefaultFlags definition. + + User can define this as any \c ValidateFlag combinations. +*/ +#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS +#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags +#endif + +//! Combination of validate flags +enum ValidateFlag { + kValidateNoFlags = 0, //!< No flags are set. + kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error. + kValidateReadFlag = 2, //!< Validation is for a read semantic. + kValidateWriteFlag = 4, //!< Validation is for a write semantic. + kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Specification +enum SchemaDraft { + kDraftUnknown = -1, + kDraftNone = 0, + kDraft03 = 3, + kDraftMin = 4, //!< Current minimum supported draft + kDraft04 = 4, + kDraft05 = 5, + kDraftMax = 5, //!< Current maximum supported draft + kDraft06 = 6, + kDraft07 = 7, + kDraft2019_09 = 8, + kDraft2020_12 = 9 +}; + +enum OpenApiVersion { + kVersionUnknown = -1, + kVersionNone = 0, + kVersionMin = 2, //!< Current minimum supported version + kVersion20 = 2, + kVersion30 = 3, + kVersionMax = 3, //!< Current maximum supported version + kVersion31 = 4, +}; + +struct Specification { + Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {} + Specification(OpenApiVersion o) : oapi(o) { + if (oapi == kVersion20) draft = kDraft04; + else if (oapi == kVersion30) draft = kDraft05; + else if (oapi == kVersion31) draft = kDraft2020_12; + else draft = kDraft04; + } + ~Specification() {} + bool IsSupported() const { + return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax))); + } + SchemaDraft draft; + OpenApiVersion oapi; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; + virtual void SetValidateFlags(unsigned flags) = 0; + virtual unsigned GetValidateFlags() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue(const ValidateErrorCode code) = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0; + virtual void Disallowed() = 0; + virtual void DisallowedWhenWriting() = 0; + virtual void DisallowedWhenReading() = 0; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + // Issue #2205 + // Hasing the key to avoid key=value cases with bug-prone zero-value hash + h ^= Hash(Hash(0, kv[i * 2]), kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0xcbf29ce4, 0x84222325), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef IValidationErrorHandler ErrorHandlerType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) : + factory(f), + error_handler(eh), + schema(s), + flags(fl), + valueSchema(), + invalidKeyword(), + invalidCode(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) { + if (validators[i]) { + factory.DestroySchemaValidator(validators[i]); + } + } + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) { + if (patternPropertiesValidators[i]) { + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + } + } + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; + const SchemaType* schema; + unsigned flags; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + ValidateErrorCode invalidCode; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + typedef IValidationErrorHandler ErrorHandler; + typedef GenericUri UriType; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) : + allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + id_(id, allocator), + spec_(schemaDocument->GetSpecification()), + pointer_(p, allocator), + typeless_(schemaDocument->GetTypeless()), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + notValidatorIndex_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false), + defaultValueLength_(0), + readOnly_(false), + writeOnly_(false), + nullable_(false) + { + GenericStringBuffer sb; + p.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString()); + + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + // PR #1393 + // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite + // recursion (with recursive schemas), since schemaDocument->getSchema() is always + // checked before creating a new one. Don't cache typeless_, though. + if (this != typeless_) { + typedef typename SchemaDocumentType::SchemaEntry SchemaEntry; + SchemaEntry *entry = schemaDocument->schemaMap_.template Push(); + new (entry) SchemaEntry(pointer_, this, true, allocator_); + schemaDocument->AddSchemaRefs(this); + } + + if (!value.IsObject()) + return; + + // If we have an id property, resolve it with the in-scope id + // Not supported for open api 2.0 or 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetIdString())) { + if (v->IsString()) { + UriType local(*v, allocator); + id_ = local.Resolve(id_, allocator); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString()); + } + } + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) { + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256u + 24]; + MemoryPoolAllocator hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + } + + if (schemaDocument) + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + + // AnyOf, OneOf, Not not supported for open api 2.0 + if (schemaDocument && spec_.oapi != kVersion20) { + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document, id_); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = typeless_; + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_); + } + } + + // PatternProperties not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + PointerType r = q.Append(itr->name, allocator_); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + // AdditionalItems not supported for openapi 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_)); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + + // ReadOnly - open api only (until draft 7 supported) + // WriteOnly - open api 3 only (until draft 7 supported) + // Both can't be true + if (spec_.oapi != kVersionNone) + AssignIfExist(readOnly_, value, GetReadOnlyString()); + if (spec_.oapi >= kVersion30) + AssignIfExist(writeOnly_, value, GetWriteOnlyString()); + if (readOnly_ && writeOnly_) + schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p); + + // Nullable - open api 3 only + // If true add 'null' as allowable type + if (spec_.oapi >= kVersion30) { + AssignIfExist(nullable_, value, GetNullableString()); + if (nullable_) + AddType(GetNullString()); + } + } + + ~Schema() { + AllocatorType::Free(enum_); + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + AllocatorType::Free(pattern_); + } +#endif + } + + const SValue& GetURI() const { + return uri_; + } + + const UriType& GetId() const { + return id_; + } + + const Specification& GetSpecification() const { + return spec_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + + bool BeginValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue"); + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = typeless_; + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set + context.arrayElementIndex++; + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems); + } + } + else + context.valueSchema = typeless_; + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue"); + // Only check pattern properties if we have validators + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + + // For enums only check if we have a hasher + if (enum_ && context.hasher) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + context.error_handler.DisallowedValue(kValidateErrorEnum); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum); + foundEnum:; + } + + // Only check allOf etc if we have validators + if (context.validatorCount > 0) { + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf); + } + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + SizeType firstMatch = 0; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) { + context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch); + } else { + oneValid = true; + firstMatch = i - oneOf_.begin; + } + } + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf); + } + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot); + } + } + + return true; + } + + bool Null(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null"); + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool b) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b); + if (!CheckBool(context, b)) + return false; + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i); + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u); + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i); + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u); + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d); + if (!(type_ & (1 << kNumberSchemaType))) { + DisallowedType(context, GetNumberString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str); + if (!(type_ & (1 << kStringSchemaType))) { + DisallowedType(context, GetStringString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength); + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength); + } + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern); + } + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject"); + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str); + + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } + } + + SizeType index = 0; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = typeless_; + return true; + } + + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + context.error_handler.DisallowedProperty(str, len); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties); + } + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject"); + if (hasRequired_) { + context.error_handler.StartMissingProperties(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired); + } + + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties); + } + + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties); + } + + if (hasDependencies_) { + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; + if (context.propertyExist[sourceIndex]) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); + } + } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); + } + + return true; + } + + bool StartArray(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray"); + context.arrayElementIndex = 0; + context.inArray = true; // Ensure we note that we are in an array + + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray"); + context.inArray = false; + + if (elementCount < minItems_) { + context.error_handler.TooFewItems(elementCount, minItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems); + } + + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(elementCount, maxItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems); + } + + return true; + } + + static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrorMultipleOf: return GetMultipleOfString(); + case kValidateErrorMaximum: return GetMaximumString(); + case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same + case kValidateErrorMinimum: return GetMinimumString(); + case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same + + case kValidateErrorMaxLength: return GetMaxLengthString(); + case kValidateErrorMinLength: return GetMinLengthString(); + case kValidateErrorPattern: return GetPatternString(); + + case kValidateErrorMaxItems: return GetMaxItemsString(); + case kValidateErrorMinItems: return GetMinItemsString(); + case kValidateErrorUniqueItems: return GetUniqueItemsString(); + case kValidateErrorAdditionalItems: return GetAdditionalItemsString(); + + case kValidateErrorMaxProperties: return GetMaxPropertiesString(); + case kValidateErrorMinProperties: return GetMinPropertiesString(); + case kValidateErrorRequired: return GetRequiredString(); + case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString(); + case kValidateErrorPatternProperties: return GetPatternPropertiesString(); + case kValidateErrorDependencies: return GetDependenciesString(); + + case kValidateErrorEnum: return GetEnumString(); + case kValidateErrorType: return GetTypeString(); + + case kValidateErrorOneOf: return GetOneOfString(); + case kValidateErrorOneOfMatch: return GetOneOfString(); // Same + case kValidateErrorAllOf: return GetAllOfString(); + case kValidateErrorAnyOf: return GetAnyOfString(); + case kValidateErrorNot: return GetNotString(); + + case kValidateErrorReadOnly: return GetReadOnlyString(); + case kValidateErrorWriteOnly: return GetWriteOnlyString(); + + default: return GetNullString(); + } + } + + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f') + RAPIDJSON_STRING_(Id, 'i', 'd') + RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r') + RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i') + RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); + if (!r->IsValid()) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + GenericRegexSearch rs(*pattern); + return rs.Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); + try { + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error& e) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); + AllocatorType::Free(r); + } + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { + return 0; + } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required. + // Also creates a hasher for enums and array uniqueness, if required. + // Also a useful place to add type-independent error checks. + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_); + context.validatorCount = validatorCount_; + + // Always return after first failure for these sub-validators + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_, false); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_, false); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_, false); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false); + } + } + + // Add any other type-independent checks here + if (readOnly_ && (context.flags & kValidateWriteFlag)) { + context.error_handler.DisallowedWhenWriting(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly); + } + if (writeOnly_ && (context.flags & kValidateReadFlag)) { + context.error_handler.DisallowedWhenReading(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly); + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckBool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return true; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_ + } + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) { + context.error_handler.NotMultipleOf(d, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + return true; + } + + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + SValue uri_; + UriType id_; + Specification spec_; + PointerType pointer_; + const SchemaType* typeless_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; + + SizeType defaultValueLength_; + + bool readOnly_; + bool writeOnly_; + bool nullable_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = static_cast(buffer[i]); + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; + virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri uri, Specification& spec) { + // Default implementation just calls through for compatibility + // Following line suppresses unused parameter warning + (void)spec; + // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi); + return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + typedef GenericValue GValue; + typedef GenericUri UriType; + typedef GenericStringRef StringRefType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + \param pointer An optional JSON pointer to the start of the schema document + \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04. + */ + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0, + const PointerType& pointer = PointerType(), // PR #1393 + const Specification& spec = Specification(kDraft04)) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + typeless_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize), + spec_(spec), + error_(kObjectType), + currentError_() + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument"); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + docId_ = UriType(uri_, allocator_); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_); + + // Establish the schema draft or open api version. + // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document. + SetSchemaSpecification(document); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call HandleRefSchema() if there are $ref. + // PR #1393 use input pointer if supplied + root_ = typeless_; + if (pointer.GetTokenCount() == 0) { + CreateSchemaRecursive(&root_, pointer, document, document, docId_); + } + else if (const ValueType* v = pointer.Get(document)) { + CreateSchema(&root_, pointer, *v, document, docId_); + } + else { + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch))); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + typeless_(rhs.typeless_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)), + docId_(std::move(rhs.docId_)), + spec_(rhs.spec_), + error_(std::move(rhs.error_)), + currentError_(std::move(rhs.currentError_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + // these may contain some allocator data so clear before deleting ownAllocator_ + uri_.SetNull(); + error_.SetNull(); + currentError_.SetNull(); + + RAPIDJSON_DELETE(ownAllocator_); + } + + const GValue& GetURI() const { return uri_; } + + const Specification& GetSpecification() const { return spec_; } + bool IsSupportedSpecification() const { return spec_.IsSupported(); } + + //! Static method to get the specification of any schema document + // Returns kDraftNone if document is silent + static const Specification GetSpecification(const ValueType& document) { + SchemaDraft draft = GetSchemaDraft(document); + if (draft != kDraftNone) + return Specification(draft); + else { + OpenApiVersion oapi = GetOpenApiVersion(document); + if (oapi != kVersionNone) + return Specification(oapi); + } + return Specification(kDraftNone); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + + //! Gets the error object. + GValue& GetError() { return error_; } + const GValue& GetError() const { return error_; } + + static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorStartUnknown: return GetStartUnknownString(); + case kSchemaErrorRefPlainName: return GetRefPlainNameString(); + case kSchemaErrorRefInvalid: return GetRefInvalidString(); + case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString(); + case kSchemaErrorRefUnknown: return GetRefUnknownString(); + case kSchemaErrorRefCyclical: return GetRefCyclicalString(); + case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString(); + case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString(); + case kSchemaErrorRegexInvalid: return GetRegexInvalidString(); + case kSchemaErrorSpecUnknown: return GetSpecUnknownString(); + case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString(); + case kSchemaErrorSpecIllegal: return GetSpecIllegalString(); + case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString(); + default: return GetNullString(); + } + } + + //! Default error method + void SchemaError(const SchemaErrorCode code, const PointerType& location) { + currentError_ = GValue(kObjectType); + AddCurrentError(code, location); + } + + //! Method for error with single string value insert + void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + AddCurrentError(code, location); + } + + //! Method for error with invalid pointer + void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + currentError_.AddMember(GetOffsetString(), static_cast(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_); + AddCurrentError(code, location); + } + + private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + typedef const PointerType* SchemaRefPtr; // PR #1393 + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void AddErrorInstanceLocation(GValue& result, const PointerType& location) { + GenericStringBuffer sb; + location.StringifyUriFragment(sb); + GValue instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), *allocator_); + result.AddMember(GetInstanceRefString(), instanceRef, *allocator_); + } + + void AddError(GValue& keyword, GValue& error) { + typename GValue::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, *allocator_); + else { + if (member->value.IsObject()) { + GValue errors(kArrayType); + errors.PushBack(member->value, *allocator_); + member->value = errors; + } + member->value.PushBack(error, *allocator_); + } + } + + void AddCurrentError(const SchemaErrorCode code, const PointerType& location) { + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code)); + currentError_.AddMember(GetErrorCodeString(), code, *allocator_); + AddErrorInstanceLocation(currentError_, location); + AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e') + RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't') + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd') + RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l') + RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e') + RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l') + RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r') + RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + +#undef RAPIDJSON_STRING_ + + // Static method to get schema draft of any schema document + static SchemaDraft GetSchemaDraft(const ValueType& document) { + static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + + if (!document.IsObject()) { + return kDraftNone; + } + + // Get the schema draft from the $schema keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kDraftUnknown; + const UriType draftUri(itr->value); + // Check base uri for match + if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04; + if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05; + if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06; + if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07; + if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03; + if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09; + if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12; + return kDraftUnknown; + } + // $schema not found + return kDraftNone; + } + + + // Get open api version of any schema document + static OpenApiVersion GetOpenApiVersion(const ValueType& document) { + static const Ch kVersion20String[] = { '2', '.', '0', '\0' }; + static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level + static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level + static SizeType len = internal::StrLen(kVersion30String); + + if (!document.IsObject()) { + return kVersionNone; + } + + // Get the open api version from the swagger / openapi keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString()); + if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kVersionUnknown; + const ValueType kVersion20Value(kVersion20String); + if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly + const ValueType kVersion30Value(kVersion30String); + if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x + const ValueType kVersion31Value(kVersion31String); + if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x + return kVersionUnknown; + } + // swagger or openapi not found + return kVersionNone; + } + + // Get the draft of the schema or the open api version (which implies the draft). + // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on. + void SetSchemaSpecification(const ValueType& document) { + // Look for '$schema', 'swagger' or 'openapi' keyword at document root + SchemaDraft docDraft = GetSchemaDraft(document); + OpenApiVersion docOapi = GetOpenApiVersion(document); + // Error if both in document + if (docDraft != kDraftNone && docOapi != kVersionNone) + SchemaError(kSchemaErrorSpecIllegal, PointerType()); + // Use document draft or open api version if present or use spec from constructor + if (docDraft != kDraftNone) + spec_ = Specification(docDraft); + else if (docOapi != kVersionNone) + spec_ = Specification(docOapi); + // Error if draft or version unknown + if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown) + SchemaError(kSchemaErrorSpecUnknown, PointerType()); + else if (!spec_.IsSupported()) + SchemaError(kSchemaErrorSpecUnsupported, PointerType()); + } + + // Changed by PR #1393 + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { + if (v.GetType() == kObjectType) { + UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id); + } + + // Changed by PR #1393 + const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { + RAPIDJSON_ASSERT(pointer.IsValid()); + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString()); + if (v.IsObject()) { + if (const SchemaType* sc = GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + } + else if (!HandleRefSchema(pointer, schema, v, document, id)) { + // The new schema constructor adds itself and its $ref(s) to schemaMap_ + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id); + if (schema) + *schema = s; + return s->GetId(); + } + } + else { + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + } + return id; + } + + // Changed by PR #1393 + // TODO should this return a UriType& ? + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) { + typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString()); + if (itr == v.MemberEnd()) + return false; + + GenericStringBuffer sb; + source.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString()); + // Resolve the source pointer to the $ref'ed schema (finally) + new (schemaRef_.template Push()) SchemaRefPtr(&source); + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len == 0) + SchemaError(kSchemaErrorRefInvalid, source); + else { + // First resolve $ref against the in-scope id + UriType scopeId = UriType(id, allocator_); + UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString()); + // See if the resolved $ref minus the fragment matches a resolved id in this document + // Search from the root. Returns the subschema in the document and its absolute JSON pointer. + PointerType basePointer = PointerType(); + const ValueType *base = FindId(document, ref, basePointer, docId_, false); + if (!base) { + // Remote reference - call the remote document provider + if (!remoteProvider_) + SchemaError(kSchemaErrorRefNoRemoteProvider, source); + else { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) { + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, absolute in the remote schema + const PointerType pointer(s, len, allocator_); + if (!pointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer); + else { + // Get the subschema + if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + return true; + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } else + // Plain name fragment, not allowed in remote schema + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + } else + SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength()); + } + } + else { // Local reference + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, relative to the resolved URI + const PointerType relPointer(s, len, allocator_); + if (!relPointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer); + else { + // Get the subschema + if (const ValueType *pv = relPointer.Get(*base)) { + // Now get the absolute JSON pointer by adding relative to base + PointerType pointer(basePointer, allocator_); + for (SizeType i = 0; i < relPointer.GetTokenCount(); i++) + pointer = pointer.Append(relPointer.GetTokens()[i], allocator_); + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); + return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } else { + // Plain name fragment, relative to the resolved URI + // Not supported in open api 2.0 and 3.0 + PointerType pointer(allocator_); + if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30) + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + // See if the fragment matches an id in this document. + // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. + else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) { + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); + return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } + } + } + + // Invalid/Unknown $ref + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + return true; + } + + //! Find the first subschema with a resolved 'id' that matches the specified URI. + // If full specified use all URI else ignore fragment. + // If found, return a pointer to the subschema and its JSON pointer. + // TODO cache pointer <-> id mapping + ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const { + SizeType i = 0; + ValueType* resval = 0; + UriType tempuri = UriType(finduri, allocator_); + UriType localuri = UriType(baseuri, allocator_); + if (doc.GetType() == kObjectType) { + // Establish the base URI of this object + typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString()); + if (m != doc.MemberEnd() && m->value.GetType() == kStringType) { + localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_); + } + // See if it matches + if (localuri.Match(finduri, full)) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString()); + resval = const_cast(&doc); + resptr = here; + return resval; + } + // No match, continue looking + for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { + if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { + resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); + } + if (resval) break; + } + } else if (doc.GetType() == kArrayType) { + // Continue looking + for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) { + if (v->GetType() == kObjectType || v->GetType() == kArrayType) { + resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_)); + } + if (resval) break; + i++; + } + } + return resval; + } + + // Added by PR #1393 + void AddSchemaRefs(SchemaType* schema) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs"); + while (!schemaRef_.Empty()) { + SchemaRefPtr *ref = schemaRef_.template Pop(1); + SchemaEntry *entry = schemaMap_.template Push(); + new (entry) SchemaEntry(**ref, schema, false, allocator_); + } + } + + // Added by PR #1393 + bool IsCyclicRef(const PointerType& pointer) const { + for (const SchemaRefPtr* ref = schemaRef_.template Bottom(); ref != schemaRef_.template End(); ++ref) + if (pointer == **ref) + return true; + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + const SchemaType* GetTypeless() const { return typeless_; } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved + GValue uri_; // Schema document URI + UriType docId_; + Specification spec_; + GValue error_; + GValue currentError_; +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator, + public internal::IValidationErrorHandler { +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; + typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator"); + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)"); + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + ResetError(); + } + + //! Reset the error state. + void ResetError() { + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); + valid_ = true; + } + + //! Implementation of ISchemaValidator + void SetValidateFlags(unsigned flags) { + flags_ = flags; + } + virtual unsigned GetValidateFlags() const { + return flags_; + } + + virtual bool IsValid() const { + if (!valid_) return false; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false; + return true; + } + //! End of Implementation of ISchemaValidator + + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } + + //! Gets the JSON pointer pointed to the invalid schema. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); + } + + //! Gets the keyword of invalid schema. + // If reporting all errors, the stack will be empty, so return "errors". + const Ch* GetInvalidSchemaKeyword() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return static_cast(GetErrorsString()); + return 0; + } + + //! Gets the error code of invalid schema. + // If reporting all errors, the stack will be empty, so return kValidateErrors. + ValidateErrorCode GetInvalidSchemaCode() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidCode; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors; + return kValidateErrorNone; + } + + //! Gets the JSON pointer pointed to the invalid value. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidDocumentPointer() const { + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + } + + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMaxLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMinLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorPattern); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalItems, true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(kValidateErrorUniqueItems, true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorRequired); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalProperties, true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) { + // Create equivalent 'required' error + ValueType error(kObjectType); + ValidateErrorCode code = kValidateErrorRequired; + error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator()); + AddErrorCode(error, code); + AddErrorInstanceLocation(error, false); + // When appending to a pointer ensure its allocator is used + PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator()); + AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator())); + ValueType wrapper(kObjectType); + wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator()); + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator()); + } + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorDependencies); + return true; + } + + void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) { + currentError_.SetObject(); + AddCurrentError(code); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorType); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf + AddErrorArray(kValidateErrorAllOf, subvalidators, count); + //for (SizeType i = 0; i < count; ++i) { + // MergeError(static_cast(subvalidators[i])->GetError()); + //} + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorAnyOf, subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorOneOf, subvalidators, count); + } + void MultipleOneOf(SizeType index1, SizeType index2) { + ValueType matches(kArrayType); + matches.PushBack(index1, GetStateAllocator()); + matches.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator()); + AddCurrentError(kValidateErrorOneOfMatch); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorNot); + } + void DisallowedWhenWriting() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorReadOnly); + } + void DisallowedWhenReading() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorWriteOnly); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's') + +#undef RAPIDJSON_STRING_ + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom());\ + valid_ = false;\ + return valid_;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\ + return valid_; + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject"); + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + valid_ = !outputHandler_ || outputHandler_->StartObject(); + return valid_; + } + + bool Key(const Ch* str, SizeType len, bool copy) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str); + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + return valid_; + } + + bool EndObject(SizeType memberCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject"); + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray"); + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + valid_ = !outputHandler_ || outputHandler_->StartArray(); + return valid_; + } + + bool EndArray(SizeType elementCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray"); + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) { + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), + depth_ + 1, + &GetStateAllocator()); + sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~static_cast(kValidateContinueOnErrorFlag)); + return sv; + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + StateAllocator::Free(p); + } + // End of implementation of ISchemaStateFactory + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, + const char* basePath, size_t basePathSize, + unsigned depth, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(depth) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : ""); + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); + return *stateAllocator_; + } + + bool GetContinueOnErrors() const { + return flags_ & kValidateContinueOnErrorFlag; + } + + bool BeginValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue"); + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + std::memset(va, 0, sizeof(ISchemaValidator*) * count); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue"); + if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb); + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom(), depth_); + void* hasher = CurrentContext().hasher; + uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast(hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + // Only check uniqueness if there is a hasher + if (hasher && context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); + // Cleanup before returning if continuing + if (GetContinueOnErrors()) { + a->PushBack(h, GetStateAllocator()); + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/'); + } + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems); + } + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema, flags_); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + void AddErrorInstanceLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + } + + void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) { + GenericStringBuffer sb; + SizeType len = CurrentSchema().GetURI().GetStringLength(); + if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch)); + if (schema.GetTokenCount()) schema.StringifyUriFragment(sb); + else GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddErrorCode(ValueType& result, const ValidateErrorCode code) { + result.AddMember(GetErrorCodeString(), code, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const ValidateErrorCode code, bool parent = false) { + AddErrorCode(currentError_, code); + AddErrorInstanceLocation(currentError_, parent); + AddErrorSchemaLocation(currentError_); + AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(code); + } + + void AddErrorArray(const ValidateErrorCode code, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(code); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; + bool valid_; + unsigned flags_; + unsigned depth_; +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + typedef GenericValue ValueType; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + error_.SetObject(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidSchemaCode_ = validator.GetInvalidSchemaCode(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + const ValueType& GetError() const { return error_; } + ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + ValidateErrorCode invalidSchemaCode_; + StackAllocator allocator_; + ValueType error_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/deps/rapidjson/rapidjson/stream.h b/deps/rapidjson/rapidjson/stream.h new file mode 100644 index 00000000000..1fd70915c54 --- /dev/null +++ b/deps/rapidjson/rapidjson/stream.h @@ -0,0 +1,223 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/deps/rapidjson/rapidjson/stringbuffer.h b/deps/rapidjson/rapidjson/stringbuffer.h new file mode 100644 index 00000000000..82ad3ca6bbf --- /dev/null +++ b/deps/rapidjson/rapidjson/stringbuffer.h @@ -0,0 +1,121 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + //! Get the size of string in bytes in the string buffer. + size_t GetSize() const { return stack_.GetSize(); } + + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/deps/rapidjson/rapidjson/uri.h b/deps/rapidjson/rapidjson/uri.h new file mode 100644 index 00000000000..f93e508a4f4 --- /dev/null +++ b/deps/rapidjson/rapidjson/uri.h @@ -0,0 +1,481 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// (C) Copyright IBM Corporation 2021 +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_URI_H_ +#define RAPIDJSON_URI_H_ + +#include "internal/strfunc.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// GenericUri + +template +class GenericUri { +public: + typedef typename ValueType::Ch Ch; +#if RAPIDJSON_HAS_STDSTRING + typedef std::basic_string String; +#endif + + //! Constructors + GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + } + + GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, len); + } + + GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, internal::StrLen(uri)); + } + + // Use with specializations of GenericValue + template GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + const Ch* u = uri.template Get(); // TypeHelper from document.h + Parse(u, internal::StrLen(u)); + } + +#if RAPIDJSON_HAS_STDSTRING + GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri.c_str(), internal::StrLen(uri.c_str())); + } +#endif + + //! Copy constructor + GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() { + *this = rhs; + } + + //! Copy constructor + GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + *this = rhs; + } + + //! Destructor. + ~GenericUri() { + Free(); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator + GenericUri& operator=(const GenericUri& rhs) { + if (this != &rhs) { + // Do not delete ownAllocator + Free(); + Allocate(rhs.GetStringLength()); + auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength()); + path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength()); + query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength()); + frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength()); + base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength()); + uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength()); + CopyPart(uri_, rhs.uri_, rhs.GetStringLength()); + } + return *this; + } + + //! Getters + // Use with specializations of GenericValue + template void Get(T& uri, Allocator& allocator) { + uri.template Set(this->GetString(), allocator); // TypeHelper from document.h + } + + const Ch* GetString() const { return uri_; } + SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen(uri_); } + const Ch* GetBaseString() const { return base_; } + SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen(base_); } + const Ch* GetSchemeString() const { return scheme_; } + SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen(scheme_); } + const Ch* GetAuthString() const { return auth_; } + SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen(auth_); } + const Ch* GetPathString() const { return path_; } + SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen(path_); } + const Ch* GetQueryString() const { return query_; } + SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen(query_); } + const Ch* GetFragString() const { return frag_; } + SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen(frag_); } + +#if RAPIDJSON_HAS_STDSTRING + static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); } + static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); } + static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); } + static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); } + static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); } + static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); } + static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); } +#endif + + //! Equality operators + bool operator==(const GenericUri& rhs) const { + return Match(rhs, true); + } + + bool operator!=(const GenericUri& rhs) const { + return !Match(rhs, true); + } + + bool Match(const GenericUri& uri, bool full = true) const { + Ch* s1; + Ch* s2; + if (full) { + s1 = uri_; + s2 = uri.uri_; + } else { + s1 = base_; + s2 = uri.base_; + } + if (s1 == s2) return true; + if (s1 == 0 || s2 == 0) return false; + return internal::StrCmp(s1, s2) == 0; + } + + //! Resolve this URI against another (base) URI in accordance with URI resolution rules. + // See https://tools.ietf.org/html/rfc3986 + // Use for resolving an id or $ref with an in-scope id. + // Returns a new GenericUri for the resolved URI. + GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) { + GenericUri resuri; + resuri.allocator_ = allocator; + // Ensure enough space for combining paths + resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash + + if (!(GetSchemeStringLength() == 0)) { + // Use all of this URI + resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength()); + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base scheme + resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength()); + if (!(GetAuthStringLength() == 0)) { + // Use this auth, path, query + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base auth + resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength()); + if (GetPathStringLength() == 0) { + // Use the base path + resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength()); + if (GetQueryStringLength() == 0) { + // Use the base query + resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength()); + } else { + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } else { + if (path_[0] == '/') { + // Absolute path - use all of this path + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } else { + // Relative path - append this path to base path after base path's last slash + size_t pos = 0; + if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) { + resuri.path_[pos] = '/'; + pos++; + } + size_t lastslashpos = baseuri.GetPathStringLength(); + while (lastslashpos > 0) { + if (baseuri.path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch)); + pos += lastslashpos; + resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } + } + // Always use this frag + resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength()); + + // Re-constitute base_ and uri_ + resuri.SetBase(); + resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1; + resuri.SetUri(); + return resuri; + } + + //! Get the allocator of this GenericUri. + Allocator& GetAllocator() { return *allocator_; } + +private: + // Allocate memory for a URI + // Returns total amount allocated + std::size_t Allocate(std::size_t len) { + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated. + // Order: scheme, auth, path, query, frag, base, uri + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + size_t total = (3 * len + 7) * sizeof(Ch); + scheme_ = static_cast(allocator_->Malloc(total)); + *scheme_ = '\0'; + auth_ = scheme_; + auth_++; + *auth_ = '\0'; + path_ = auth_; + path_++; + *path_ = '\0'; + query_ = path_; + query_++; + *query_ = '\0'; + frag_ = query_; + frag_++; + *frag_ = '\0'; + base_ = frag_; + base_++; + *base_ = '\0'; + uri_ = base_; + uri_++; + *uri_ = '\0'; + return total; + } + + // Free memory for a URI + void Free() { + if (scheme_) { + Allocator::Free(scheme_); + scheme_ = 0; + } + } + + // Parse a URI into constituent scheme, authority, path, query, & fragment parts + // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per + // https://tools.ietf.org/html/rfc3986 + void Parse(const Ch* uri, std::size_t len) { + std::size_t start = 0, pos1 = 0, pos2 = 0; + Allocate(len); + + // Look for scheme ([^:/?#]+):)? + if (start < len) { + while (pos1 < len) { + if (uri[pos1] == ':') break; + pos1++; + } + if (pos1 != len) { + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (pos1 < pos2) { + pos1++; + std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch)); + scheme_[pos1] = '\0'; + start = pos1; + } + } + } + // Look for auth (//([^/?#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + auth_ = scheme_ + GetSchemeStringLength(); + auth_++; + *auth_ = '\0'; + if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') { + pos2 = start + 2; + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch)); + auth_[pos2 - start] = '\0'; + start = pos2; + } + // Look for path ([^?#]*) + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + path_ = auth_ + GetAuthStringLength(); + path_++; + *path_ = '\0'; + if (start < len) { + pos2 = start; + while (pos2 < len) { + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch)); + path_[pos2 - start] = '\0'; + if (path_[0] == '/') + RemoveDotSegments(); // absolute path - normalize + start = pos2; + } + } + // Look for query (\?([^#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + query_ = path_ + GetPathStringLength(); + query_++; + *query_ = '\0'; + if (start < len && uri[start] == '?') { + pos2 = start + 1; + while (pos2 < len) { + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch)); + query_[pos2 - start] = '\0'; + start = pos2; + } + } + // Look for fragment (#(.*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + frag_ = query_ + GetQueryStringLength(); + frag_++; + *frag_ = '\0'; + if (start < len && uri[start] == '#') { + std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); + frag_[len - start] = '\0'; + } + + // Re-constitute base_ and uri_ + base_ = frag_ + GetFragStringLength() + 1; + SetBase(); + uri_ = base_ + GetBaseStringLength() + 1; + SetUri(); + } + + // Reconstitute base + void SetBase() { + Ch* next = base_; + std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch)); + next+= GetSchemeStringLength(); + std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch)); + next+= GetAuthStringLength(); + std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch)); + next+= GetPathStringLength(); + std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch)); + next+= GetQueryStringLength(); + *next = '\0'; + } + + // Reconstitute uri + void SetUri() { + Ch* next = uri_; + std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch)); + next+= GetBaseStringLength(); + std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch)); + next+= GetFragStringLength(); + *next = '\0'; + } + + // Copy a part from one GenericUri to another + // Return the pointer to the next part to be copied to + Ch* CopyPart(Ch* to, Ch* from, std::size_t len) { + RAPIDJSON_ASSERT(to != 0); + RAPIDJSON_ASSERT(from != 0); + std::memcpy(to, from, len * sizeof(Ch)); + to[len] = '\0'; + Ch* next = to + len + 1; + return next; + } + + // Remove . and .. segments from the path_ member. + // https://tools.ietf.org/html/rfc3986 + // This is done in place as we are only removing segments. + void RemoveDotSegments() { + std::size_t pathlen = GetPathStringLength(); + std::size_t pathpos = 0; // Position in path_ + std::size_t newpos = 0; // Position in new path_ + + // Loop through each segment in original path_ + while (pathpos < pathlen) { + // Get next segment, bounded by '/' or end + size_t slashpos = 0; + while ((pathpos + slashpos) < pathlen) { + if (path_[pathpos + slashpos] == '/') break; + slashpos++; + } + // Check for .. and . segments + if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') { + // Backup a .. segment in the new path_ + // We expect to find a previously added slash at the end or nothing + RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/'); + size_t lastslashpos = newpos; + // Make sure we don't go beyond the start segment + if (lastslashpos > 1) { + // Find the next to last slash and back up to it + lastslashpos--; + while (lastslashpos > 0) { + if (path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + // Set the new path_ position + newpos = lastslashpos; + } + } else if (slashpos == 1 && path_[pathpos] == '.') { + // Discard . segment, leaves new path_ unchanged + } else { + // Move any other kind of segment to the new path_ + RAPIDJSON_ASSERT(newpos <= pathpos); + std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch)); + newpos += slashpos; + // Add slash if not at end + if ((pathpos + slashpos) < pathlen) { + path_[newpos] = '/'; + newpos++; + } + } + // Move to next segment + pathpos += slashpos + 1; + } + path_[newpos] = '\0'; + } + + Ch* uri_; // Everything + Ch* base_; // Everything except fragment + Ch* scheme_; // Includes the : + Ch* auth_; // Includes the // + Ch* path_; // Absolute if starts with / + Ch* query_; // Includes the ? + Ch* frag_; // Includes the # + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Uri. +}; + +//! GenericUri for Value (UTF-8, default allocator). +typedef GenericUri Uri; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_URI_H_ diff --git a/deps/rapidjson/rapidjson/writer.h b/deps/rapidjson/rapidjson/writer.h new file mode 100644 index 00000000000..632e02ce74a --- /dev/null +++ b/deps/rapidjson/rapidjson/writer.h @@ -0,0 +1,721 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteNanAndInfNullFlag = 4, //!< Allow writing of Infinity, -Infinity and NaN as null. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + + static const size_t kDefaultLevelDepth = 32; + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag) && !(writeFlags & kWriteNanAndInfNullFlag)) + return false; + if (writeFlags & kWriteNanAndInfNullFlag) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); + return true; + } + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (kWriteDefaultFlags & kWriteNanAndInfNullFlag) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); + return true; + } + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/deps/rapidjson/version.txt b/deps/rapidjson/version.txt new file mode 100644 index 00000000000..f0680970560 --- /dev/null +++ b/deps/rapidjson/version.txt @@ -0,0 +1,5 @@ +Source: +https://github.com/Tencent/rapidjson + +Git tag: +6089180 \ No newline at end of file diff --git a/src/netlist/CMakeLists.txt b/src/netlist/CMakeLists.txt index 1d011425c5f..95fca302f58 100644 --- a/src/netlist/CMakeLists.txt +++ b/src/netlist/CMakeLists.txt @@ -17,7 +17,7 @@ target_include_directories(netlist $ $ ${Z3_INCLUDE_DIRS} - ${RAPIDJSON_INCLUDEDIR} + $ ) target_compile_options(netlist diff --git a/src/utilities/CMakeLists.txt b/src/utilities/CMakeLists.txt index 2c585b216da..9d5a95a49f8 100644 --- a/src/utilities/CMakeLists.txt +++ b/src/utilities/CMakeLists.txt @@ -9,7 +9,7 @@ target_include_directories(utilities $ $ $ - ${RAPIDJSON_INCLUDEDIR} + $ ) target_compile_options(utilities PUBLIC ${COMPILE_OPTIONS_PUBLIC} From 3594541516e53f6dacdd90b40d1d0c04a90f3410 Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 22 Jan 2024 15:43:53 +0100 Subject: [PATCH 46/66] Bugfixes after new implementing ModuleModel event handler --- .../gui/basic_tree_model/base_tree_item.h | 2 +- .../include/gui/module_model/module_item.h | 16 +- .../include/gui/module_model/module_model.h | 104 ++- plugins/gui/src/module_model/module_item.cpp | 107 ++- plugins/gui/src/module_model/module_model.cpp | 630 ++++++++++-------- .../gui/src/module_widget/module_widget.cpp | 9 +- 6 files changed, 492 insertions(+), 376 deletions(-) diff --git a/plugins/gui/include/gui/basic_tree_model/base_tree_item.h b/plugins/gui/include/gui/basic_tree_model/base_tree_item.h index 39fc926381e..04df7bf683e 100644 --- a/plugins/gui/include/gui/basic_tree_model/base_tree_item.h +++ b/plugins/gui/include/gui/basic_tree_model/base_tree_item.h @@ -189,7 +189,7 @@ namespace hal */ virtual int getOwnRow(); - private: + protected: BaseTreeItem* mParent; QList mChildren; }; diff --git a/plugins/gui/include/gui/module_model/module_item.h b/plugins/gui/include/gui/module_model/module_item.h index 49bbdc9288b..f34143e088c 100644 --- a/plugins/gui/include/gui/module_model/module_item.h +++ b/plugins/gui/include/gui/module_model/module_item.h @@ -107,6 +107,13 @@ namespace hal */ bool highlighted() const; + /** + * Checks if this ModuleItem is direct child to mRootItem + * + * @return + */ + bool isToplevelItem() const; + /** * Gets the type of the netlist item this ModuleItem represents. * @@ -121,6 +128,12 @@ namespace hal */ void setName(const QString& name); + /** + * Set the module type name (gate types are immutable) + * @param moduleType + */ + void setModuleType(const QString& moduleType); + /** * Marks/Unmarks this ModuleItem as highlighted. * @@ -131,8 +144,9 @@ namespace hal private: u32 mId; - TreeItemType mType; + TreeItemType mItemType; QString mName; + QString mModuleType; bool mHighlighted; }; diff --git a/plugins/gui/include/gui/module_model/module_model.h b/plugins/gui/include/gui/module_model/module_model.h index edd4ecd76e8..fd0e5c01755 100644 --- a/plugins/gui/include/gui/module_model/module_model.h +++ b/plugins/gui/include/gui/module_model/module_model.h @@ -36,6 +36,7 @@ #include #include #include +#include namespace hal { @@ -50,6 +51,28 @@ namespace hal { Q_OBJECT + class TempGateAssignment + { + int mAccumulate; + public: + QHash mGateRemove; + QHash mGateAssign; + + TempGateAssignment() : mAccumulate(0) {;} + void removeGateFromModule(u32 gateId, Module* m) + { + if (!mGateRemove.contains(gateId)) + mGateRemove[gateId] = m; + } + void assignGateToModule(u32 gateId, Module* m) + { + mGateAssign[gateId] = m; + } + bool isAccumulate() const { return mAccumulate > 0; } + void beginAccumulate() { mAccumulate++; } + void endAccumulate() { mAccumulate--; } + }; + public: /** * Constructor.
@@ -90,14 +113,6 @@ namespace hal */ ModuleItem* getItem(const QModelIndex& index) const; - /** - * Returns the index where the specified ModuleItem can be found. - * - * @param item - The ModuleItem to search for in the item model - * @returns the model index of the specified ModuleItem - */ - QModelIndex getIndex(const ModuleItem* const item) const; - /** * Returns the ModuleItem for a specified id and type. * @@ -123,7 +138,7 @@ namespace hal * @param id - The id of the module to add. * @param parent_module - The id of the parent module of the module to add. */ - void addModule(const u32 id, const u32 parent_module); + void addModule(const u32 id, const u32 parentId); /** * Add a gate to the item model. For the specified gate a new ModuleItem is created and stored. @@ -131,7 +146,7 @@ namespace hal * @param id - The id of the gate to add. * @param parent_module - The id of the parent module of the gate to add. */ - void addGate(const u32 id, const u32 parent_module); + void addGate(const u32 id, const u32 parentId); /** * Add a net to the item model. For the specified net a new ModuleItem is created and stored. @@ -139,7 +154,7 @@ namespace hal * @param id - The id of the net to add. * @param parent_module - The id of the parent module of the net to add. */ - void addNet(const u32 id, const u32 parent_module); + void addNet(const u32 id, const u32 parentId); /** * Recursively adds the given module with all of its submodules (and their submodules and so on...) @@ -148,7 +163,7 @@ namespace hal * @param module - The module which should be added to the item model together with all its * submodules, gates and nets. */ - void addRecursively(const Module* module); + void addRecursively(const Module* module, BaseTreeItem* parentItem = nullptr); /** * Removes a module from the item model. The specified module MUST be contained in the item model. @@ -174,9 +189,14 @@ namespace hal /** * Handles the assigment of gates to modules. * If the gate does not yet exist in the item model, a new one is created. - * All nets, that are connected to the gate, will be updated. */ - void handleModuleGateAssinged(const u32 id, const u32 parent_module); + void moduleAssignGate(const u32 moduleId, const u32 gateId); + + /** + * Handles the assigment of nets to modules for nets connected to gates of list. + * If the gate list is empty all nets connected to modules in tree are considered. + */ + void moduleAssignNets(const QList& gateIds = QList()); /** * Updates the position of a net in the ModuleTree. @@ -186,7 +206,7 @@ namespace hal * * @param net The net whose source or destination might have changed. */ - void updateNet(const Net* net, const QHash* parentAssignment = nullptr); + void updateNetParent(const Net* net, const QHash* parentAssignment = nullptr); /** * Reattaches the ModuleItem corresponding to the specified module to a new parent item. @@ -226,8 +246,6 @@ namespace hal */ bool isModifying(); - void debugDump() const; - private Q_SLOTS: void handleModuleNameChanged(Module* mod); @@ -242,7 +260,11 @@ namespace hal * * @param module The module whose parent has changed. */ - void handleModuleParentChanged(const Module* module); + void handleModuleParentChanged(const Module* mod); + + void handleModuleSubmoduleAdded(Module* mod, u32 submodId); + + void handleModuleSubmoduleRemoved(Module* mod, u32 submodId); void handleModuleGateAssigned(Module* mod, u32 gateId); @@ -252,14 +274,18 @@ namespace hal void handleModuleGatesAssignEnd(Module* mod, u32 numberGates); + void handleGateCreated(Gate* gat); + void handleGateNameChanged(Gate* gat); - void handleNetCreated(Net* net); + void handleGateRemoved(Gate* gat); - void handleNetRemoved(Net* net); + void handleNetCreated(Net* net); void handleNetNameChanged(Net* net); + void handleNetRemoved(Net* net); + void handleNetUpdated(Net* net, u32 data); private: /** @@ -269,17 +295,41 @@ namespace hal * @param net The net for which a new parent should be searched. * * @return The new parent module, that contains all sources and destinations of net. If no such parent could be found - * (e.g. net has no sources or destinations), nullptr is returned instead. + * (e.g. global input/output, net has no sources or destinations), nullptr is returned instead. */ - Module* findNetParent(const Net* net); - void updateAllNets(); + Module* findNetParent(const Net* net) const; + + /** + * Recursion to loop over all modules from (sub-)tree and create a hash assign how internal nets are assigned to module + * @param parent - top tree element + * @param parentAssignment - the resulting net module assignment + * @param assignedNets - nets already assigned (no assignment to module higher up in hierarchy) + */ void findNetParentRecursion(BaseTreeItem* parent, QHash& parentAssignment, std::unordered_set& assignedNets) const; - QMap mModuleMap; - QMap mGateMap; - QMap mNetMap; - std::array*, 3> mModuleItemMaps = {&mModuleMap, &mGateMap, &mNetMap};; + /** + * Factory method to append new tree items. Insert signals are sent to view. New items are put into hash table. + * @param id - ID of new tree item + * @param itemType - Whether new tree item is module, gate, or net + * @param parentItem - Parent to new tree item. Will create top-level item if parent is nullptr + * @return Point to new tree item + */ + ModuleItem* createChildItem(u32 id, ModuleItem::TreeItemType itemType, BaseTreeItem* parentItem = nullptr); + + /** + * Method to remove and delete tree items. Remove signals are sent to view. + * Hash table will _NOT_ be updated since caller can do it more efficiently. + * @param itemToRemove - Item to be removed from tree + * @param parentItem - Parent item. Must be present. + */ + void removeChildItem(ModuleItem* itemToRemove, BaseTreeItem* parentItem); + + QMultiMap mModuleMap; + QMultiMap mGateMap; + QMultiMap mNetMap; + QMultiMap* mModuleItemMaps[3] = {&mModuleMap, &mGateMap, &mNetMap};; bool mIsModifying; + TempGateAssignment mTempGateAssignment; }; } // namespace hal diff --git a/plugins/gui/src/module_model/module_item.cpp b/plugins/gui/src/module_model/module_item.cpp index cf5a79bcc56..8291ac52b80 100644 --- a/plugins/gui/src/module_model/module_item.cpp +++ b/plugins/gui/src/module_model/module_item.cpp @@ -11,21 +11,35 @@ namespace hal ModuleItem::ModuleItem(const u32 id, const TreeItemType type) : BaseTreeItem(), mId(id), - mType(type), + mItemType(type), mHighlighted(false) { switch(type) { case TreeItemType::Module: - mName = QString::fromStdString(gNetlist->get_module_by_id(id)->get_name()); + { + const Module* m = gNetlist->get_module_by_id(id); + Q_ASSERT(m); + mName = QString::fromStdString(m->get_name()); + mModuleType = QString::fromStdString(m->get_type()); break; + } case TreeItemType::Gate: - mName = QString::fromStdString(gNetlist->get_gate_by_id(id)->get_name()); + { + const Gate* g = gNetlist->get_gate_by_id(id); + Q_ASSERT(g); + mName = QString::fromStdString(g->get_name()); + mModuleType = QString::fromStdString(g->get_type()->get_name()); break; + } case TreeItemType::Net: - mName = QString::fromStdString(gNetlist->get_net_by_id(id)->get_name()); + { + const Net* n = gNetlist->get_net_by_id(id); + Q_ASSERT(n); + mName = QString::fromStdString(n->get_name()); break; } + } } int ModuleItem::row() const @@ -38,7 +52,7 @@ ModuleItem::ModuleItem(const u32 id, const TreeItemType type) : void ModuleItem::appendExistingChildIfAny(const QMap& moduleMap) { - if(mType != TreeItemType::Module) // only module can have children + if(mItemType != TreeItemType::Module) // only module can have children return; Module* m = gNetlist->get_module_by_id(mId); @@ -55,30 +69,25 @@ ModuleItem::ModuleItem(const u32 id, const TreeItemType type) : } } + void ModuleItem::setModuleType(const QString &moduleType) + { + if (mItemType != TreeItemType::Module) return; + Module* module = gNetlist->get_module_by_id(mId); + if (!module) return; + module->set_type(moduleType.toStdString()); + mModuleType = moduleType; + } + QVariant ModuleItem::getData(int column) const { // DEBUG CODE, USE STYLED DELEGATES OR SOMETHING - if(column == 0) + switch (column) { + case 0: return mName; - else if (column == 1) + case 1: return mId; - else if(column == 2) - { - switch(mType) - { - case TreeItemType::Module: - { - Module* module = gNetlist->get_module_by_id(mId); - if(!module) - return QVariant(); - return QString::fromStdString(module->get_type()); - } - case TreeItemType::Gate: - Gate* gate = gNetlist->get_gate_by_id(mId); - if(!gate) - return QVariant(); - return QString::fromStdString(gate->get_type()->get_name()); - } + case 2: + return mModuleType; } return QVariant(); } @@ -86,42 +95,21 @@ ModuleItem::ModuleItem(const u32 id, const TreeItemType type) : void ModuleItem::setData(QList data) { setName(data[0].toString()); - switch(mType) - { - case TreeItemType::Module: - { - Module* module = gNetlist->get_module_by_id(mId); - if(!module) - return; - module->set_type(data[3].toString().toStdString()); - } - case TreeItemType::Gate: - return; - } + if (mItemType == TreeItemType::Module) + setModuleType(data.at(2).toString()); } void ModuleItem::setDataAtIndex(int index, QVariant &data) { - if(index == 0) { + switch (index) { + case 0: setName(data.toString()); return; - } - else if (index == 1) + case 1: + return; + case 2: + setModuleType(data.toString()); return; - else if(index == 2) - { - switch(mType) - { - case TreeItemType::Module: - { - Module* module = gNetlist->get_module_by_id(mId); - if(!module) - return; - module->set_type(data.toString().toStdString()); - } - case TreeItemType::Gate: - return; - } } } @@ -140,8 +128,14 @@ ModuleItem::ModuleItem(const u32 id, const TreeItemType type) : return mHighlighted; } + bool ModuleItem::isToplevelItem() const + { + if (dynamic_cast(mParent)) return true; + return false; + } + ModuleItem::TreeItemType ModuleItem::getType() const{ - return mType; + return mItemType; } void ModuleItem::setName(const QString& name) @@ -159,5 +153,8 @@ ModuleItem::ModuleItem(const u32 id, const TreeItemType type) : return 3; } - void ModuleItem::appendData(QVariant data) {} + void ModuleItem::appendData(QVariant data) + { + Q_UNUSED(data); + } } diff --git a/plugins/gui/src/module_model/module_model.cpp b/plugins/gui/src/module_model/module_model.cpp index 103c8784dc6..15042327714 100644 --- a/plugins/gui/src/module_model/module_model.cpp +++ b/plugins/gui/src/module_model/module_model.cpp @@ -16,6 +16,8 @@ namespace hal connect(gNetlistRelay, &NetlistRelay::moduleCreated, this, &ModuleModel::handleModuleCreated); connect(gNetlistRelay, &NetlistRelay::moduleNameChanged, this, &ModuleModel::handleModuleNameChanged); connect(gNetlistRelay, &NetlistRelay::moduleParentChanged, this, &ModuleModel::handleModuleParentChanged); + connect(gNetlistRelay, &NetlistRelay::moduleSubmoduleAdded, this, &ModuleModel::handleModuleSubmoduleAdded); + connect(gNetlistRelay, &NetlistRelay::moduleSubmoduleRemoved, this, &ModuleModel::handleModuleSubmoduleRemoved); connect(gNetlistRelay, &NetlistRelay::moduleGateAssigned, this, &ModuleModel::handleModuleGateAssigned); connect(gNetlistRelay, &NetlistRelay::moduleGatesAssignBegin, this, &ModuleModel::handleModuleGatesAssignBegin); connect(gNetlistRelay, &NetlistRelay::moduleGatesAssignEnd, this, &ModuleModel::handleModuleGatesAssignEnd); @@ -91,43 +93,10 @@ namespace hal return nullptr; } - QModelIndex ModuleModel::getIndex(const ModuleItem* const item) const - { - assert(item); - - QVector row_numbers; - const ModuleItem* current_item = item; - - while (current_item != mRootItem->getChild(0)) - { - row_numbers.append(current_item->row()); - current_item = static_cast(current_item->getParent()); - } - - QModelIndex model_index = index(0, 0, QModelIndex()); - - for (QVector::const_reverse_iterator i = row_numbers.crbegin(); i != row_numbers.crend(); ++i) - model_index = index(*i, 0, model_index); - - return model_index; - } - void ModuleModel::init() { - ModuleItem* item = new ModuleItem(1); - mModuleMap.insert(1, item); - - beginInsertRows(index(0, 0, QModelIndex()), 0, 0); - mRootItem->appendChild(item); - endInsertRows(); - - Module* m = gNetlist->get_top_module(); - addRecursively(m); - for(auto net : gNetlist->get_top_module()->get_internal_nets()) - { - addNet(net->get_id(), m->get_id()); - updateNet(net); - } + addRecursively(gNetlist->get_top_module()); + moduleAssignNets(); } void ModuleModel::clear() @@ -138,137 +107,114 @@ namespace hal mModuleMap.clear(); mGateMap.clear(); mNetMap.clear(); - //TODO : clear colors endResetModel(); } - void ModuleModel::addModule(u32 id, u32 parent_module) + void ModuleModel::addModule(u32 id, u32 parentId) { - assert(gNetlist->get_module_by_id(id)); - assert(gNetlist->get_module_by_id(parent_module)); - assert(!mModuleMap.contains(id)); - assert(mModuleMap.contains(parent_module)); - - ModuleItem* item = new ModuleItem(id); - ModuleItem* parent = mModuleMap.value(parent_module); - - item->setParent(parent); - mModuleMap.insert(id, item); - - QModelIndex index = getIndex(parent); + Q_ASSERT(gNetlist->get_module_by_id(id)); + Q_ASSERT(gNetlist->get_module_by_id(parentId)); - int row = parent->getChildCount(); - mIsModifying = true; - beginInsertRows(index, row, row); - parent->appendChild(item); - mIsModifying = false; - endInsertRows(); + for (auto it = mModuleMap.lowerBound(parentId); it != mModuleMap.upperBound(parentId); ++it) + { + ModuleItem* parentItem = mModuleMap.value(parentId); + createChildItem(id, ModuleItem::TreeItemType::Module, parentItem); + } } - void ModuleModel::addGate(u32 id, u32 parent_module) + void ModuleModel::addGate(u32 id, u32 parentId) { - assert(gNetlist->get_gate_by_id(id)); - assert(gNetlist->get_module_by_id(parent_module)); - assert(!mGateMap.contains(id)); - assert(mModuleMap.contains(parent_module)); + Q_ASSERT(gNetlist->get_gate_by_id(id)); + Q_ASSERT(gNetlist->get_module_by_id(parentId)); - ModuleItem* item = new ModuleItem(id, ModuleItem::TreeItemType::Gate); - ModuleItem* parent = mModuleMap.value(parent_module); - item->setParent(parent); - mGateMap.insert(id, item); - - QModelIndex index = getIndex(parent); - - int row = parent->getChildCount(); - mIsModifying = true; - beginInsertRows(index, row, row); - parent->appendChild(item); - mIsModifying = false; - endInsertRows(); + for (auto it = mModuleMap.lowerBound(parentId); it != mModuleMap.upperBound(parentId); ++it) + { + createChildItem(id, ModuleItem::TreeItemType::Gate, it.value()); + } } - void ModuleModel::addNet(u32 id, u32 parent_module) + void ModuleModel::addNet(u32 id, u32 parentId) { - assert(gNetlist->get_net_by_id(id)); - assert(gNetlist->get_module_by_id(parent_module)); - assert(!mNetMap.contains(id)); - assert(mModuleMap.contains(parent_module)); - - ModuleItem* item = new ModuleItem(id, ModuleItem::TreeItemType::Net); - ModuleItem* parent = mModuleMap.value(parent_module); + Q_ASSERT(gNetlist->get_net_by_id(id)); + Q_ASSERT(gNetlist->get_module_by_id(parentId)); - item->setParent(parent); - mNetMap.insert(id, item); - - QModelIndex index = getIndex(parent); - - int row = parent->getChildCount(); - mIsModifying = true; - beginInsertRows(index, row, row); - parent->appendChild(item); - mIsModifying = false; - endInsertRows(); + for (auto it = mModuleMap.lowerBound(parentId); it != mModuleMap.upperBound(parentId); ++it) + { + createChildItem(id, ModuleItem::TreeItemType::Net, it.value()); + } } - void ModuleModel::addRecursively(const Module* module) + void ModuleModel::addRecursively(const Module* module, BaseTreeItem *parentItem) { - if(!module->is_top_module()) - addModule(module->get_id(), module->get_parent_module()->get_id()); - for(auto &m : module->get_submodules()) - addRecursively(m); + Q_ASSERT(module); + ModuleItem* moduleItem = createChildItem(module->get_id(), ModuleItem::TreeItemType::Module, parentItem ? parentItem : mRootItem); + Q_ASSERT(moduleItem); + for(const Module* subModule : module->get_submodules()) + addRecursively(subModule, moduleItem); - for(auto &g : module->get_gates()) - addGate(g->get_id(), module->get_id()); + for(const Gate* g : module->get_gates()) + createChildItem(g->get_id(), ModuleItem::TreeItemType::Gate, moduleItem); } void ModuleModel::removeModule(const u32 id) { - assert(id != 1); - // module was most likely already purged from netlist - assert(mModuleMap.contains(id)); + auto it = mModuleMap.lowerBound(id); + while (it != mModuleMap.upperBound(id)) + { + ModuleItem* item = mModuleMap.value(id); + BaseTreeItem* parentItem = item->getParent(); - ModuleItem* item = mModuleMap.value(id); - ModuleItem* parent = static_cast(item->getParent()); - assert(item); - assert(parent); + removeChildItem(item,parentItem); - QModelIndex index = getIndex(parent); + it = mModuleMap.erase(it); + } + } - int row = item->row(); + void ModuleModel::removeGate(const u32 id) + { + auto it = mGateMap.lowerBound(id); + while (it != mGateMap.upperBound(id)) + { + ModuleItem* item = it.value(); + BaseTreeItem* parentItem = item->getParent(); - mIsModifying = true; - beginRemoveRows(index, row, row); - parent->removeChild(item); - mIsModifying = false; - endRemoveRows(); + removeChildItem(item, parentItem); - mModuleMap.remove(id); - delete item; + it = mGateMap.erase(it); + } } - void ModuleModel::removeGate(const u32 id) + void ModuleModel::removeNet(const u32 id) { - //assert(gNetlist->get_gate_by_id(id)); - assert(mGateMap.contains(id)); + auto it = mNetMap.lowerBound(id); + while (it != mNetMap.upperBound(id)) + { + ModuleItem* item = it.value(); + BaseTreeItem* parentItem = item->getParent(); + + removeChildItem(item, parentItem); + + it = mNetMap.erase(it); + } + } - ModuleItem* item = mGateMap.value(id); - ModuleItem* parent = static_cast(item->getParent()); - assert(item); - assert(parent); + void ModuleModel::removeChildItem(ModuleItem *itemToRemove, BaseTreeItem *parentItem) + { + Q_ASSERT(itemToRemove); + Q_ASSERT(parentItem); - QModelIndex index = getIndex(parent); + QModelIndex index = getIndexFromItem(parentItem); - int row = item->row(); + int row = itemToRemove->row(); mIsModifying = true; beginRemoveRows(index, row, row); - parent->removeChild(item); - mIsModifying = false; + parentItem->removeChild(itemToRemove); endRemoveRows(); + mIsModifying = false; - mGateMap.remove(id); - delete item; + delete itemToRemove; } void ModuleModel::handleModuleNameChanged(Module* mod) @@ -289,25 +235,73 @@ namespace hal void ModuleModel::handleModuleGateAssigned(Module* mod, u32 gateId) { - handleModuleGateAssinged(gateId, mod->get_id()); + if (mTempGateAssignment.isAccumulate()) + mTempGateAssignment.assignGateToModule(gateId,mod); + else + { + moduleAssignGate(mod->get_id(), gateId); + moduleAssignNets({gateId}); + } } void ModuleModel::handleModuleGateRemoved(Module* mod, u32 gateId) { - Q_UNUSED(mod); - removeGate(gateId); + if (mTempGateAssignment.isAccumulate()) + mTempGateAssignment.removeGateFromModule(gateId,mod); + else + { + auto it = mGateMap.lowerBound(gateId); + while (it != mGateMap.upperBound(gateId)) + { + ModuleItem* item = it.value(); + if (!item->isToplevelItem()) + { + ModuleItem* parentItem = static_cast(item->getParent()); + if (parentItem->id() == mod->get_id()) + { + removeChildItem(item, parentItem); + it = mGateMap.erase(it); + continue; + } + } + ++it; + } + } } void ModuleModel::handleModuleGatesAssignBegin(Module* mod, u32 numberGates) { Q_UNUSED(mod); Q_UNUSED(numberGates); + mTempGateAssignment.beginAccumulate(); } void ModuleModel::handleModuleGatesAssignEnd(Module* mod, u32 numberGates) { Q_UNUSED(mod); Q_UNUSED(numberGates); + mTempGateAssignment.endAccumulate(); + if (!mTempGateAssignment.isAccumulate()) + { + for (auto it = mTempGateAssignment.mGateAssign.begin(); it != mTempGateAssignment.mGateAssign.end(); ++it) + { + moduleAssignGate(it.value()->get_id(), it.key()); // moduleId, gateId + } + moduleAssignNets(mTempGateAssignment.mGateAssign.keys()); + mTempGateAssignment.mGateAssign.clear(); + mTempGateAssignment.mGateRemove.clear(); + } + } + + void ModuleModel::handleGateRemoved(Gate* gat) + { + removeGate(gat->get_id()); + } + + void ModuleModel::handleGateCreated(Gate* gat) + { + Module* mod = gat->get_module(); + if (mod) moduleAssignGate(mod->get_id(), gat->get_id()); } void ModuleModel::handleGateNameChanged(Gate* gat) @@ -333,41 +327,32 @@ namespace hal void ModuleModel::handleNetUpdated(Net* net, u32 data) { Q_UNUSED(data); - updateNet(net); + updateNetParent(net); } - void ModuleModel::removeNet(const u32 id) + void ModuleModel::handleModuleParentChanged(const Module* mod) { - //assert(gNetlist->get_net_by_id(id)); - if(!mNetMap.contains(id)) // global nets are not contained in the item model - return; - - ModuleItem* item = mNetMap.value(id); - ModuleItem* parent = static_cast(item->getParent()); - assert(item); - assert(parent); - - QModelIndex index = getIndex(parent); + Q_ASSERT(mod); + updateModuleParent(mod); - int row = item->row(); - - mIsModifying = true; - beginRemoveRows(index, row, row); - parent->removeChild(item); - mIsModifying = false; - endRemoveRows(); + QHash parentAssignment; + std::unordered_set assignedNets; + findNetParentRecursion(mRootItem, parentAssignment, assignedNets); - mNetMap.remove(id); - delete item; + for(Net* net : mod->get_nets()) + updateNetParent(net, &parentAssignment); } - void ModuleModel::handleModuleParentChanged(const Module* module) + void ModuleModel::handleModuleSubmoduleAdded(Module* mod, u32 submodId) { - assert(module); - updateModuleParent(module); + Q_UNUSED(mod); + Q_UNUSED(submodId); + } - for(Net* net : module->get_nets()) - updateNet(net); + void ModuleModel::handleModuleSubmoduleRemoved(Module* mod, u32 submodId) + { + Q_UNUSED(mod); + Q_UNUSED(submodId); } void ModuleModel::findNetParentRecursion(BaseTreeItem* parent, QHash &parentAssignment, std::unordered_set& assignedNets) const @@ -379,7 +364,7 @@ namespace hal findNetParentRecursion(item, parentAssignment, assignedNets); Module* m = gNetlist->get_module_by_id(item->id()); Q_ASSERT(m); - std::unordered_set internalNets = m->get_internal_nets(); + std::unordered_set internalNets = m->get_nets(); if (!internalNets.empty()) { for (Net* n : assignedNets) @@ -397,159 +382,254 @@ namespace hal } } - void ModuleModel::debugDump() const + Module* ModuleModel::findNetParent(const Net *net) const { - QHash parentAssignment; - std::unordered_set assignedNets; - findNetParentRecursion(mRootItem, parentAssignment, assignedNets); + QHash modHash; + if (net->is_global_input_net() || net->is_global_output_net()) return nullptr; + int maxDepth = 0; + + for (const Endpoint* ep : net->get_sources()) + { + Module* m = ep->get_gate()->get_module(); + Q_ASSERT(m); + int depth = m->get_submodule_depth(); + if (depth > maxDepth) maxDepth = depth; + modHash.insert(m,depth); + } - QTextStream xout(stdout, QIODevice::WriteOnly); - for (auto it = mModuleMap.begin(); it != mModuleMap.end(); ++it) + for (const Endpoint* ep : net->get_destinations()) { - xout << it.value()->id() << " " << it.value()->name() << " "; + Module* m = ep->get_gate()->get_module(); + Q_ASSERT(m); + int depth = m->get_submodule_depth(); + if (depth > maxDepth) maxDepth = depth; + modHash.insert(m,depth); + } - for (auto jt = parentAssignment.constBegin(); jt != parentAssignment.constEnd(); ++jt) + while (modHash.size() > 1 && maxDepth > 0) + { + auto it = modHash.begin(); + while (it != modHash.end()) { - if (it.value() != jt.value()) continue; - xout << " <" << jt.key()->get_id() << ">"; + if (it.value() == maxDepth) + { + Module* parentMod = it.key()->get_parent_module(); + modHash.erase(it); + if (parentMod) modHash.insert(parentMod,maxDepth-1); + break; + } + ++it; } - xout << "\n"; + if (it == modHash.end()) + --maxDepth; } - xout.flush(); + if (modHash.empty()) return nullptr; + return modHash.begin().key(); } - void ModuleModel::handleModuleGateAssinged(const u32 id, const u32 parent_module) + void ModuleModel::moduleAssignGate(const u32 moduleId, const u32 gateId) { // Don't need new function handleModuleGateRemoved(), because the GateAssinged event always follows GateRemoved // or NetlistInternalManager updates Net connections when a gate is deleted. - if(!mGateMap.contains(id)) - addGate(id, parent_module); + QSet parentsHandled; + Q_ASSERT(gNetlist->get_gate_by_id(gateId)); + + auto itGat = mGateMap.lowerBound(gateId); + while (itGat != mGateMap.upperBound(gateId)) + { + ModuleItem* gatItem = itGat.value(); + if (gatItem->isToplevelItem()) continue; + ModuleItem* oldParentItem = static_cast(gatItem->getParent()); + Q_ASSERT(oldParentItem); + + if (oldParentItem->id() != moduleId) + { + removeChildItem(gatItem,oldParentItem); + itGat = mGateMap.erase(itGat); + } + else + { + parentsHandled.insert(oldParentItem); + ++itGat; + } + + } + + if (!moduleId) return; + for (auto itMod = mModuleMap.lowerBound(moduleId); itMod != mModuleMap.upperBound(moduleId); ++itMod) + { + ModuleItem* parentItem = itMod.value(); + if (parentsHandled.contains(parentItem)) continue; + createChildItem(gateId, ModuleItem::TreeItemType::Gate, parentItem); + } + + } + void ModuleModel::moduleAssignNets(const QList& gateIds) + { QHash parentAssignment; std::unordered_set assignedNets; findNetParentRecursion(mRootItem, parentAssignment, assignedNets); - Gate* gate = gNetlist->get_gate_by_id(id); - for(Net* in_net : gate->get_fan_in_nets()) - updateNet(in_net, &parentAssignment); - for(Net* in_net : gate->get_fan_out_nets()) - updateNet(in_net, &parentAssignment); + QSet netsToAssign; + if (gateIds.isEmpty()) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + netsToAssign = QSet(parentAssignment.keys().begin(),parentAssignment.keys().end()); +#else + netsToAssign = parentAssignment.keys().toSet(); +#endif + } + else + { + for (u32 id : gateIds) + { + Gate* gate = gNetlist->get_gate_by_id(id); + for(Net* in_net : gate->get_fan_in_nets()) + netsToAssign.insert(in_net); + for(Net* out_net : gate->get_fan_out_nets()) + netsToAssign.insert(out_net); + } + } + + for (const Net* n: netsToAssign) + updateNetParent(n, &parentAssignment); } - void ModuleModel::updateNet(const Net* net, const QHash *parentAssignment) + void ModuleModel::updateNetParent(const Net* net, const QHash *parentAssignment) { - assert(net); - u32 id = net->get_id(); - - if(!mNetMap.contains(id)) - return; - - ModuleItem* item = mNetMap.value(id); - ModuleItem* oldParentItem = static_cast(item->getParent()); - assert(oldParentItem); + Q_ASSERT(net); + u32 netId = net->get_id(); - ModuleItem* newParentItem = nullptr; + QSet parentsHandled; + u32 newParentId = 0; if (parentAssignment) - newParentItem = parentAssignment->value(net); + { + ModuleItem* modItem = parentAssignment->value(net); + if (modItem) + newParentId = modItem->id(); + } else { Module* newParentModule = findNetParent(net); - if(newParentModule == nullptr) - newParentModule = gNetlist->get_top_module(); - newParentItem = mModuleMap[newParentModule->get_id()]; + if (newParentModule) + newParentId = newParentModule->get_id(); } - if (!newParentItem) + auto itNet = mNetMap.lowerBound(netId); + while (itNet != mNetMap.upperBound(netId)) { - mIsModifying = true; - oldParentItem->removeChild(item); - mIsModifying = false; - return; - } + if (itNet.value()->isToplevelItem()) continue; + + ModuleItem* netItem = itNet.value(); + ModuleItem* oldParentItem = static_cast(netItem->getParent()); + Q_ASSERT(oldParentItem); - if(newParentItem->id() == oldParentItem->id()) - return; - QModelIndex newIndex = getIndex(newParentItem); - QModelIndex oldIndex = getIndex(oldParentItem); - int row = item->row(); + if (newParentId == 0 || newParentId != oldParentItem->id()) + { + removeChildItem(netItem,oldParentItem); + itNet = mNetMap.erase(itNet); + } + else + { + parentsHandled.insert(oldParentItem); + ++itNet; + } + } - mIsModifying = true; - beginMoveRows(oldIndex, row, row, newIndex, newParentItem->getChildCount()); - oldParentItem->removeChild(item); - newParentItem->appendChild(item); - mIsModifying = false; - endMoveRows(); + if (!newParentId) return; + for (auto itMod = mModuleMap.lowerBound(newParentId); itMod != mModuleMap.upperBound(newParentId); ++itMod) + { + ModuleItem* parentItem = itMod.value(); + if (parentsHandled.contains(parentItem)) continue; + createChildItem(net->get_id(), ModuleItem::TreeItemType::Net, parentItem); + } } - void ModuleModel::updateModuleParent(const Module* module){ - assert(module); + void ModuleModel::updateModuleParent(const Module* module) + { + Q_ASSERT(module); u32 id = module->get_id(); - assert(id != 1); - assert(mModuleMap.contains(id)); - ModuleItem* item = mModuleMap.value(id); - ModuleItem* oldParent = static_cast(item->getParent()); - assert(oldParent); + Q_ASSERT(id != 1); - assert(module->get_parent_module()); - if(oldParent->id() == module->get_parent_module()->get_id()) - return; + QSet parentsHandled; + u32 parentId = module->get_parent_module()->get_id(); + Q_ASSERT(parentId > 0); - assert(mModuleMap.contains(module->get_parent_module()->get_id())); - ModuleItem* newParent = mModuleMap.value(module->get_parent_module()->get_id()); + auto itSubm = mModuleMap.lowerBound(id); + while (itSubm != mModuleMap.upperBound(id)) + { + ModuleItem* submItem = itSubm.value(); + if (submItem->isToplevelItem()) continue; + ModuleItem* oldParentItem = static_cast(submItem->getParent()); + Q_ASSERT(oldParentItem); - QModelIndex oldIndex = getIndex(oldParent); - QModelIndex newIndex = getIndex(newParent); - int row = item->row(); + if (oldParentItem->id() != parentId) + { + removeChildItem(submItem,oldParentItem); + itSubm = mModuleMap.erase(itSubm); + } + else + { + parentsHandled.insert(oldParentItem); + ++itSubm; + } + } - mIsModifying = true; - beginMoveRows(oldIndex, row, row, newIndex, newParent->getChildCount()); - oldParent->removeChild(item); - newParent->appendChild(item); - mIsModifying = false; - endMoveRows(); + if (!parentId) return; + for (auto itMod = mModuleMap.lowerBound(parentId); itMod != mModuleMap.upperBound(parentId); ++itMod) + { + ModuleItem* parentItem = itMod.value(); + if (parentsHandled.contains(parentItem)) continue; + addRecursively(module, parentItem); + } } void ModuleModel::updateModuleName(u32 id) { - assert(gNetlist->get_module_by_id(id)); - assert(mModuleMap.contains(id)); + Q_ASSERT(gNetlist->get_module_by_id(id)); - ModuleItem* item = mModuleMap.value(id); - assert(item); + for (auto it = mModuleMap.lowerBound(id); it != mModuleMap.upperBound(id); ++it) + { + ModuleItem* item = it.value(); + Q_ASSERT(item); - item->setName(QString::fromStdString(gNetlist->get_module_by_id(id)->get_name())); // REMOVE & ADD AGAIN + item->setName(QString::fromStdString(gNetlist->get_module_by_id(id)->get_name())); // REMOVE & ADD AGAIN - QModelIndex index = getIndex(item); - Q_EMIT dataChanged(index, index); + QModelIndex index = getIndexFromItem(item); + Q_EMIT dataChanged(index, index); + } } void ModuleModel::updateGateName(u32 id) { - assert(gNetlist->get_gate_by_id(id)); - assert(mGateMap.contains(id)); + Q_ASSERT(gNetlist->get_gate_by_id(id)); - ModuleItem* item = mGateMap.value(id); - assert(item); + for (auto it = mGateMap.lowerBound(id); it != mGateMap.upperBound(id); ++it) + { + ModuleItem* item = it.value(); + Q_ASSERT(item); - item->setName(QString::fromStdString(gNetlist->get_gate_by_id(id)->get_name())); // REMOVE & ADD AGAIN + item->setName(QString::fromStdString(gNetlist->get_gate_by_id(id)->get_name())); // REMOVE & ADD AGAIN - QModelIndex index = getIndex(item); - Q_EMIT dataChanged(index, index); + QModelIndex index = getIndexFromItem(item); + Q_EMIT dataChanged(index, index); + } } void ModuleModel::updateNetName(u32 id) { - assert(gNetlist->get_net_by_id(id)); - assert(mNetMap.contains(id)); + Q_ASSERT(gNetlist->get_net_by_id(id)); + Q_ASSERT(mNetMap.contains(id)); ModuleItem* item = mNetMap.value(id); - assert(item); + Q_ASSERT(item); item->setName(QString::fromStdString(gNetlist->get_net_by_id(id)->get_name())); // REMOVE & ADD AGAIN - QModelIndex index = getIndex(item); + QModelIndex index = getIndexFromItem(item); Q_EMIT dataChanged(index, index); } @@ -558,45 +638,27 @@ namespace hal return mModuleItemMaps[(int)type]->value(id); } - bool ModuleModel::isModifying() + ModuleItem* ModuleModel::createChildItem(u32 id, ModuleItem::TreeItemType itemType, BaseTreeItem *parentItem) { - return mIsModifying; - } - - Module* ModuleModel::findNetParent(const Net* net){ - // cannot use Module::get_internal_nets(), because currently that function is implemented so, - // that a net can be "internal" to multiple modules at the same depth. - // => instead manually search for deepest module, that contains all sources and destinations of net. - assert(net); - if(net->get_num_of_sources() == 0 && net->get_num_of_destinations() == 0) - return nullptr; - - std::vector endpoints = net->get_sources(); + ModuleItem* retval = new ModuleItem(id, itemType); + mModuleItemMaps[(int)itemType]->insertMulti(id,retval); - { - std::vector destinations = net->get_destinations(); - endpoints.insert(endpoints.end(), destinations.begin(), destinations.end()); - } - - Module* parent = endpoints[0]->get_gate()->get_module(); - endpoints.erase(endpoints.begin()); + if (!parentItem) parentItem = mRootItem; + QModelIndex index = getIndexFromItem(parentItem); + int row = parentItem->getChildCount(); + mIsModifying = true; + beginInsertRows(index, row, row); + parentItem->appendChild(retval); + endInsertRows(); + mIsModifying = false; - // might want to split up endpoints, if sources and destinations should be handled differently - while(endpoints.size() > 0) - { - std::vector::iterator it = endpoints.begin(); - while(it != endpoints.end()) - { - if(parent->contains_gate((*it)->get_gate(), true)) - it = endpoints.erase(it); - else - ++it; - } + return retval; + } - if(endpoints.size() > 0) - parent = parent->get_parent_module(); - } - return parent; + bool ModuleModel::isModifying() + { + return mIsModifying; } + } diff --git a/plugins/gui/src/module_widget/module_widget.cpp b/plugins/gui/src/module_widget/module_widget.cpp index aaf294f42bc..0ecaa5212b2 100644 --- a/plugins/gui/src/module_widget/module_widget.cpp +++ b/plugins/gui/src/module_widget/module_widget.cpp @@ -245,10 +245,6 @@ namespace hal QAction delete_action; QAction extractPythonAction; QAction focus_in_view; - QAction debug_dump; - debug_dump.setText("Debug dump"); - debug_dump.setParent(&context_menu); - context_menu.addAction(&debug_dump); switch(type) { @@ -391,9 +387,6 @@ namespace hal } } - if (clicked == &debug_dump) - mModuleModel->debugDump(); - if (clicked == &change_type_action) gNetlistRelay->changeModuleType(getModuleItemFromIndex(index)->id()); @@ -618,7 +611,7 @@ namespace hal for (auto module_id : gSelectionRelay->selectedModulesList()) { - QModelIndex index = mModuleProxyModel->mapFromSource(mModuleModel->getIndex(mModuleModel->getItem(module_id))); + QModelIndex index = mModuleProxyModel->mapFromSource(mModuleModel->getIndexFromItem(mModuleModel->getItem(module_id))); module_selection.select(index, index); } From f0dd87efcaf209069935c49665548eb09f2dbe9e Mon Sep 17 00:00:00 2001 From: Nils Albartus Date: Wed, 24 Jan 2024 10:34:12 +0100 Subject: [PATCH 47/66] removed rapidjson from deps; added bitwuzla latest version compatibility --- cmake/detect_dependencies.cmake | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cmake/detect_dependencies.cmake b/cmake/detect_dependencies.cmake index 6ee82c749e4..67ced3bebfe 100644 --- a/cmake/detect_dependencies.cmake +++ b/cmake/detect_dependencies.cmake @@ -27,11 +27,22 @@ find_package(Sanitizers REQUIRED) # ############################### # #### Bitwuzla # ############################### -find_package(Bitwuzla) +pkg_check_modules(BITWUZLA bitwuzla) + +# find_package(Bitwuzla) + +if(BITWUZLA_FOUND) + message(STATUS "Found BITWUZLA") + message(STATUS " BITWUZLA_LIBRARIES: ${BITWUZLA_LIBRARIES}") + message(STATUS " BITWUZLA_LINK_LIBRARIES: ${BITWUZLA_LINK_LIBRARIES}") + message(STATUS " BITWUZLA_INCLUDE_DIRS: ${BITWUZLA_INCLUDE_DIRS}") +else() + set(BITWUZLA_LIBRARY "") + set(BITWUZLA_INCLUDE_DIRS "") + + message(STATUS "Bitwuzla not found, but this is optional...") +endif(BITWUZLA_FOUND) -if(Bitwuzla_FOUND) - set(BITWUZLA_LIBRARY Bitwuzla::bitwuzla) -endif() # ############################### # #### OpenMP From 17d7ddd9707ac928b035af7398ab5ef15f4447c9 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 24 Jan 2024 16:21:49 +0100 Subject: [PATCH 48/66] Overhaul of Module::assign_pin_net() which was too expansive --- include/hal_core/netlist/module.h | 3 +- plugins/gui/src/module_model/module_model.cpp | 72 ++++++++++++++++++- src/netlist/module.cpp | 71 +++++++++--------- src/netlist/netlist.cpp | 9 --- src/python_bindings/bindings/netlist.cpp | 2 +- 5 files changed, 104 insertions(+), 53 deletions(-) diff --git a/include/hal_core/netlist/module.h b/include/hal_core/netlist/module.h index c2347d209e1..bd4b833553a 100644 --- a/include/hal_core/netlist/module.h +++ b/include/hal_core/netlist/module.h @@ -734,6 +734,7 @@ namespace hal std::unordered_map*> m_pin_groups_map; std::unordered_map*> m_pin_group_names_map; std::list*> m_pin_groups_ordered; + u32 m_pin_number[4] = { 0, 0, 0, 0 }; /* stores gates sorted by id */ std::unordered_map m_gates_map; @@ -748,7 +749,7 @@ namespace hal NetConnectivity check_net_endpoints(const Net* net) const; Result check_net(Net* net, bool recursive = false); - Result assign_pin_net(const u32 pin_id, Net* net, PinDirection direction, const std::string& name = "", PinType type = PinType::none); + bool assign_pin_net(const u32 pin_id, Net* net, PinDirection direction); bool remove_pin_net(Net* net); Result create_pin_internal(const u32 id, const std::string& name, Net* net, PinDirection direction, PinType type, bool force_name); bool delete_pin_internal(ModulePin* pin); diff --git a/plugins/gui/src/module_model/module_model.cpp b/plugins/gui/src/module_model/module_model.cpp index 15042327714..b671e8e7a9d 100644 --- a/plugins/gui/src/module_model/module_model.cpp +++ b/plugins/gui/src/module_model/module_model.cpp @@ -204,6 +204,21 @@ namespace hal Q_ASSERT(itemToRemove); Q_ASSERT(parentItem); + while (itemToRemove->getChildCount()) + { + ModuleItem* childItem = static_cast(itemToRemove->getChildren().at(0)); + int ityp = static_cast(childItem->getType()); + auto it = mModuleItemMaps[ityp]->lowerBound(childItem->id()); + while (it != mModuleItemMaps[ityp]->upperBound(childItem->id())) + { + if (it.value() == childItem) + it = mModuleItemMaps[ityp]->erase(it); + else + ++it; + } + removeChildItem(childItem,itemToRemove); + } + QModelIndex index = getIndexFromItem(parentItem); int row = itemToRemove->row(); @@ -550,6 +565,9 @@ namespace hal void ModuleModel::updateModuleParent(const Module* module) { + ModuleItem* moduleItemToBeMoved = nullptr; + bool moduleItemReassigned = false; + Q_ASSERT(module); u32 id = module->get_id(); Q_ASSERT(id != 1); @@ -568,8 +586,27 @@ namespace hal if (oldParentItem->id() != parentId) { - removeChildItem(submItem,oldParentItem); - itSubm = mModuleMap.erase(itSubm); + if (moduleItemToBeMoved) + { + // remove tree item recursively + removeChildItem(submItem,oldParentItem); + itSubm = mModuleMap.erase(itSubm); + } + else + { + // save tree item for reassignment + moduleItemToBeMoved = submItem; + QModelIndex index = getIndexFromItem(oldParentItem); + + int row = submItem->row(); + + mIsModifying = true; + beginRemoveRows(index, row, row); + oldParentItem->removeChild(submItem); + endRemoveRows(); + mIsModifying = false; + ++itSubm; + } } else { @@ -583,8 +620,37 @@ namespace hal { ModuleItem* parentItem = itMod.value(); if (parentsHandled.contains(parentItem)) continue; - addRecursively(module, parentItem); + if (moduleItemToBeMoved && !moduleItemReassigned) + { + QModelIndex index = getIndexFromItem(parentItem); + int row = parentItem->getChildCount(); + mIsModifying = true; + beginInsertRows(index, row, row); + parentItem->appendChild(moduleItemToBeMoved); + endInsertRows(); + mIsModifying = false; + moduleItemReassigned = true; + } + else + { + addRecursively(module, parentItem); + } + } + + if (moduleItemToBeMoved && !moduleItemReassigned) + { + // stored item could not be reassigned, delete it + auto it = mModuleMap.lowerBound(id); + while (it != mModuleMap.upperBound(id)) + { + if (it.value() == moduleItemToBeMoved) + it = mModuleMap.erase(it); + else + ++it; + } + delete moduleItemToBeMoved; } + } void ModuleModel::updateModuleName(u32 id) diff --git a/src/netlist/module.cpp b/src/netlist/module.cpp index 07daa6caddf..74da03ce416 100644 --- a/src/netlist/module.cpp +++ b/src/netlist/module.cpp @@ -671,9 +671,9 @@ namespace hal } else { - if (auto res = assign_pin_net(get_unique_pin_id(), net, PinDirection::inout); res.is_error()) + if (!assign_pin_net(get_unique_pin_id(), net, PinDirection::inout)) { - return ERR(res.get_error()); + return ERR("could not assign inout pin to net ID " + std::to_string(net->get_id())+ ": failed to create pin"); } } } @@ -693,9 +693,9 @@ namespace hal else { m_input_nets.insert(net); - if (auto res = assign_pin_net(get_unique_pin_id(), net, PinDirection::input); res.is_error()) + if (!assign_pin_net(get_unique_pin_id(), net, PinDirection::input)) { - return ERR(res.get_error()); + return ERR("could not assign input pin to net ID " + std::to_string(net->get_id())+ ": failed to create pin"); } } } @@ -715,9 +715,9 @@ namespace hal else { m_output_nets.insert(net); - if (auto res = assign_pin_net(get_unique_pin_id(), net, PinDirection::output); res.is_error()) + if (!assign_pin_net(get_unique_pin_id(), net, PinDirection::output)) { - return ERR(res.get_error()); + return ERR("could not assign output pin to net ID " + std::to_string(net->get_id())+ ": failed to create pin"); } } } @@ -1546,44 +1546,34 @@ namespace hal return true; } - Result Module::assign_pin_net(const u32 pin_id, Net* net, PinDirection direction, const std::string& name, PinType type) + bool Module::assign_pin_net(const u32 pin_id, Net* net, PinDirection direction) { - std::string name_internal; + std::string port_prefix; - if (!name.empty()) + switch (direction) { - name_internal = name; - } - else - { - std::string port_prefix; - u32 ctr = 0; - switch (direction) - { - case PinDirection::input: - port_prefix = "I"; - break; - case PinDirection::inout: - port_prefix = "IO"; - break; - case PinDirection::output: - port_prefix = "O"; - break; - default: - return ERR("could not assign pin '" + name_internal + "' to net: invalid pin direction '" + enum_to_string(direction) + "'"); - } - do - { - name_internal = port_prefix + "(" + std::to_string(ctr) + ")"; - ctr++; - } while (m_pin_names_map.find(name_internal) != m_pin_names_map.end() || m_pin_group_names_map.find(name_internal) != m_pin_group_names_map.end()); + case PinDirection::input: + port_prefix = "I"; + break; + case PinDirection::inout: + port_prefix = "IO"; + break; + case PinDirection::output: + port_prefix = "O"; + break; + default: + log_warning("module", "could not assign pin to net ID {}: invalid pin direction '{}'", net->get_id(), enum_to_string(direction)); + return false; } + std::string name_internal = port_prefix + "(" + std::to_string(m_pin_number[(int)direction]) + ")"; + // create pin ModulePin* pin; - if (auto res = create_pin_internal(pin_id, name_internal, net, direction, type, false); res.is_error()) + if (auto res = create_pin_internal(pin_id, name_internal, net, direction, PinType::none, false); res.is_error()) { - return ERR_APPEND(res.get_error(), "could not assign pin '" + name_internal + "' to net: failed to create pin"); + log_warning("module", "could not assign pin '{}' to net: failed to create pin", name_internal); + return false; } else { @@ -1592,18 +1582,20 @@ namespace hal if (const auto group_res = create_pin_group_internal(get_unique_pin_group_id(), name_internal, pin->get_direction(), pin->get_type(), true, 0, false); group_res.is_error()) { - return ERR_APPEND(group_res.get_error(), "could not assign pin '" + name_internal + "' to net: failed to create pin group"); + log_warning("module", "could not assign pin '{}' to net: failed to create pin group", name_internal); + return false; } else { if (!group_res.get()->assign_pin(pin)) { - return ERR("could not assign pin '" + name_internal + "' to net: failed to assign pin to pin group"); + log_warning("module", "could not assign pin '{}' to net: failed to assign pin to pin group", name_internal); + return false; } } PinChangedEvent(this,PinEvent::GroupCreate,pin->get_group().first->get_id()).send(); - return OK(pin); + return true; } bool Module::remove_pin_net(Net* net) @@ -1690,6 +1682,7 @@ namespace hal m_pins.push_back(std::move(pin_owner)); m_pins_map[id] = pin; m_pin_names_map[name] = pin; + ++m_pin_number[(int)direction]; // mark pin ID as used if (auto free_id_it = m_free_pin_ids.find(id); free_id_it != m_free_pin_ids.end()) diff --git a/src/netlist/netlist.cpp b/src/netlist/netlist.cpp index bc73520dd93..7e233a04f70 100644 --- a/src/netlist/netlist.cpp +++ b/src/netlist/netlist.cpp @@ -567,15 +567,6 @@ namespace hal return create_module(get_unique_module_id(), name, parent, gates); } - Module* Netlist::create_module_python(const std::string &name, Module *parent, const std::vector &gates) - { - CALLGRIND_START_INSTRUMENTATION; - CALLGRIND_TOGGLE_COLLECT; - return create_module(get_unique_module_id(), name, parent, gates); - CALLGRIND_TOGGLE_COLLECT; - CALLGRIND_STOP_INSTRUMENTATION; - } - bool Netlist::delete_module(Module* module) { return m_manager->delete_module(module); diff --git a/src/python_bindings/bindings/netlist.cpp b/src/python_bindings/bindings/netlist.cpp index 1a15960835a..3b30eb2b882 100644 --- a/src/python_bindings/bindings/netlist.cpp +++ b/src/python_bindings/bindings/netlist.cpp @@ -500,7 +500,7 @@ namespace hal )"); py_netlist.def("create_module", - &Netlist::create_module_python, + py::overload_cast&>(&Netlist::create_module), py::arg("name"), py::arg("parent"), py::arg("gates") = std::vector(), From 35bc94becca29e6e5896d07a6b29764ba204d532 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 24 Jan 2024 16:31:11 +0100 Subject: [PATCH 49/66] Remove VALGRIND header and helper function --- include/hal_core/netlist/netlist.h | 1 - src/netlist/netlist.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/hal_core/netlist/netlist.h b/include/hal_core/netlist/netlist.h index 4ecadfc8006..415db8badbf 100644 --- a/include/hal_core/netlist/netlist.h +++ b/include/hal_core/netlist/netlist.h @@ -473,7 +473,6 @@ namespace hal * @returns The new module on success, nullptr otherwise. */ Module* create_module(const std::string& name, Module* parent, const std::vector& gates = {}); - Module* create_module_python(const std::string& name, Module* parent, const std::vector& gates = {}); /** * Remove a module from the netlist. diff --git a/src/netlist/netlist.cpp b/src/netlist/netlist.cpp index 7e233a04f70..24a364b8992 100644 --- a/src/netlist/netlist.cpp +++ b/src/netlist/netlist.cpp @@ -7,7 +7,6 @@ #include "hal_core/netlist/net.h" #include "hal_core/netlist/netlist_internal_manager.h" #include "hal_core/utilities/log.h" -#include namespace hal { From 31a7e7f89ad78519515b279f40bd7a99c8d4d7dc Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 29 Jan 2024 12:22:31 +0100 Subject: [PATCH 50/66] bugfix: must not decrement 'busy'-counter on Python created views --- plugins/gui/include/gui/python/python_editor.h | 2 ++ plugins/gui/src/python/python_editor.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/gui/include/gui/python/python_editor.h b/plugins/gui/include/gui/python/python_editor.h index 4e07f167a12..5a8e2fba537 100644 --- a/plugins/gui/include/gui/python/python_editor.h +++ b/plugins/gui/include/gui/python/python_editor.h @@ -848,5 +848,7 @@ namespace hal SettingsItemKeybind* mSettingSaveFileAs; SettingsItemKeybind* mSettingRunFile; SettingsItemKeybind* mSettingCreateFile; + + QList mBlockedContextIds; }; } // namespace hal diff --git a/plugins/gui/src/python/python_editor.cpp b/plugins/gui/src/python/python_editor.cpp index 54e11dcfba5..b986c7c716f 100644 --- a/plugins/gui/src/python/python_editor.cpp +++ b/plugins/gui/src/python/python_editor.cpp @@ -852,8 +852,9 @@ namespace hal // Update snapshots when clicking on run this->updateSnapshots(); - for (const auto& ctx : gGraphContextManager->getContexts()) + for (GraphContext* ctx : gGraphContextManager->getContexts()) { + mBlockedContextIds.append(ctx->id()); ctx->beginChange(); } @@ -862,9 +863,10 @@ namespace hal void PythonEditor::handleThreadFinished() { - for (const auto& ctx : gGraphContextManager->getContexts()) + for (u32 ctxId : mBlockedContextIds) { - ctx->endChange(); + GraphContext* ctx = gGraphContextManager->getContextById(ctxId); + if (ctx) ctx->endChange(); } mFileModifiedBar->setHidden(true); From d1f4279a5e77d71eacd294caf722cc84abbcd433 Mon Sep 17 00:00:00 2001 From: joern274 Date: Tue, 30 Jan 2024 17:53:20 +0100 Subject: [PATCH 51/66] bugfix: don't iterate over volatile list --- plugins/gui/src/module_model/module_model.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/gui/src/module_model/module_model.cpp b/plugins/gui/src/module_model/module_model.cpp index b671e8e7a9d..48c74c21202 100644 --- a/plugins/gui/src/module_model/module_model.cpp +++ b/plugins/gui/src/module_model/module_model.cpp @@ -491,7 +491,8 @@ namespace hal if (gateIds.isEmpty()) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - netsToAssign = QSet(parentAssignment.keys().begin(),parentAssignment.keys().end()); + QList tempKeys = parentAssignment.keys(); + netsToAssign = QSet(tempKeys.begin(),tempKeys.end()); #else netsToAssign = parentAssignment.keys().toSet(); #endif From de83430a59cee52998dac7ec506d7ff769be9c88 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 31 Jan 2024 19:46:10 +0100 Subject: [PATCH 52/66] Make sure dock widget gets inserted with correct index --- plugins/gui/include/gui/docking_system/tab_widget.h | 6 ++++++ .../gui/src/content_layout_area/content_layout_area.cpp | 7 +++++++ plugins/gui/src/content_manager/content_manager.cpp | 2 +- plugins/gui/src/docking_system/tab_widget.cpp | 5 +++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/plugins/gui/include/gui/docking_system/tab_widget.h b/plugins/gui/include/gui/docking_system/tab_widget.h index d5d56f5ea50..4881f0838da 100644 --- a/plugins/gui/include/gui/docking_system/tab_widget.h +++ b/plugins/gui/include/gui/docking_system/tab_widget.h @@ -116,6 +116,12 @@ namespace hal */ void handleNoCurrentWidget(int index); + /** + * Returns the number of widgets / buttons as of mDockBar->count() + * @return - The number of widgets / buttons + */ + int widgetCount() const; + /** * Removes all buttons from the dockbar and therefore the widgets from the area. The corresponding * widgets are not destroyed but hidden. diff --git a/plugins/gui/src/content_layout_area/content_layout_area.cpp b/plugins/gui/src/content_layout_area/content_layout_area.cpp index 8a3ac9b66f2..37f116bb4bb 100644 --- a/plugins/gui/src/content_layout_area/content_layout_area.cpp +++ b/plugins/gui/src/content_layout_area/content_layout_area.cpp @@ -1,5 +1,6 @@ #include "gui/content_layout_area/content_layout_area.h" +#include "hal_core/utilities/log.h" #include "gui/docking_system/dock_bar.h" #include "gui/docking_system/splitter_anchor.h" #include "gui/docking_system/tab_widget.h" @@ -109,6 +110,12 @@ namespace hal void ContentLayoutArea::addContent(ContentWidget* widget, int index, content_anchor anchor) { + if (index > mTabWidget->widgetCount()) + { + log_warning("gui", "Cannot insert widget '{}' at index {}, moved to index {}", widget->name().toStdString(), index, mTabWidget->widgetCount()); + index = mTabWidget->widgetCount(); + } + switch (anchor) { case content_anchor::center: diff --git a/plugins/gui/src/content_manager/content_manager.cpp b/plugins/gui/src/content_manager/content_manager.cpp index 8b2d13a7a0a..79f1e0e65b0 100644 --- a/plugins/gui/src/content_manager/content_manager.cpp +++ b/plugins/gui/src/content_manager/content_manager.cpp @@ -153,7 +153,7 @@ namespace hal mExternalIndex = 6; mGraphTabWidget = new GraphTabWidget(); - mMainWindow->addContent(mGraphTabWidget, 2, content_anchor::center); + mMainWindow->addContent(mGraphTabWidget, 0, content_anchor::center); mModuleWidget = new ModuleWidget(); mMainWindow->addContent(mModuleWidget, 0, content_anchor::left); diff --git a/plugins/gui/src/docking_system/tab_widget.cpp b/plugins/gui/src/docking_system/tab_widget.cpp index 15602f4f18f..42fb55b218d 100644 --- a/plugins/gui/src/docking_system/tab_widget.cpp +++ b/plugins/gui/src/docking_system/tab_widget.cpp @@ -201,4 +201,9 @@ namespace hal if (mDockBar->unused()) hide(); } + + int TabWidget::widgetCount() const + { + return mDockBar->count(); + } } From 7be5957ef354b8516e66aaf1347d88bc432f6f60 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 31 Jan 2024 21:09:52 +0100 Subject: [PATCH 53/66] Make sure dock widget gets inserted with correct index (fixed) --- .../gui/content_anchor/content_anchor.h | 5 ++++ .../gui/docking_system/splitter_anchor.h | 2 +- .../include/gui/docking_system/tab_widget.h | 2 +- .../content_layout_area.cpp | 24 ++++++++++++++++--- .../src/docking_system/splitter_anchor.cpp | 2 +- plugins/gui/src/docking_system/tab_widget.cpp | 2 +- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/plugins/gui/include/gui/content_anchor/content_anchor.h b/plugins/gui/include/gui/content_anchor/content_anchor.h index eefe744c322..8fb03c1c8dd 100644 --- a/plugins/gui/include/gui/content_anchor/content_anchor.h +++ b/plugins/gui/include/gui/content_anchor/content_anchor.h @@ -84,6 +84,11 @@ namespace hal * Destructor that has to be overriden. */ virtual inline ~ContentAnchor() = 0; + + /** + * Returns the number of widgets / buttons as of mDockBar->count() + */ + virtual int count() const = 0; }; ContentAnchor::~ContentAnchor() diff --git a/plugins/gui/include/gui/docking_system/splitter_anchor.h b/plugins/gui/include/gui/docking_system/splitter_anchor.h index 7678f0c01f6..d7bd083c071 100644 --- a/plugins/gui/include/gui/docking_system/splitter_anchor.h +++ b/plugins/gui/include/gui/docking_system/splitter_anchor.h @@ -113,7 +113,7 @@ namespace hal * * @return The number of widgets. */ - int count(); + int count() const override; /** * Removes all buttons from the dockbar and therefore the widgets from the area. The corresponding diff --git a/plugins/gui/include/gui/docking_system/tab_widget.h b/plugins/gui/include/gui/docking_system/tab_widget.h index 4881f0838da..9692b36c411 100644 --- a/plugins/gui/include/gui/docking_system/tab_widget.h +++ b/plugins/gui/include/gui/docking_system/tab_widget.h @@ -120,7 +120,7 @@ namespace hal * Returns the number of widgets / buttons as of mDockBar->count() * @return - The number of widgets / buttons */ - int widgetCount() const; + int count() const override; /** * Removes all buttons from the dockbar and therefore the widgets from the area. The corresponding diff --git a/plugins/gui/src/content_layout_area/content_layout_area.cpp b/plugins/gui/src/content_layout_area/content_layout_area.cpp index 37f116bb4bb..8dc4d88d102 100644 --- a/plugins/gui/src/content_layout_area/content_layout_area.cpp +++ b/plugins/gui/src/content_layout_area/content_layout_area.cpp @@ -110,12 +110,30 @@ namespace hal void ContentLayoutArea::addContent(ContentWidget* widget, int index, content_anchor anchor) { - if (index > mTabWidget->widgetCount()) + int maxIndex = 0; + switch (anchor) { - log_warning("gui", "Cannot insert widget '{}' at index {}, moved to index {}", widget->name().toStdString(), index, mTabWidget->widgetCount()); - index = mTabWidget->widgetCount(); + case content_anchor::center: + maxIndex = mTabWidget->count(); + break; + case content_anchor::left: + maxIndex = mLeftAnchor->count(); + break; + case content_anchor::right: + maxIndex = mRightAnchor->count(); + break; + case content_anchor::bottom: + maxIndex = mBottomAnchor->count(); + break; + } + + if (index > maxIndex) + { + log_warning("gui", "Cannot insert widget '{}' at index {}, moved to index {}", widget->name().toStdString(), index, maxIndex); + index = maxIndex; } + switch (anchor) { case content_anchor::center: diff --git a/plugins/gui/src/docking_system/splitter_anchor.cpp b/plugins/gui/src/docking_system/splitter_anchor.cpp index 996ef138e21..5500e80a87f 100644 --- a/plugins/gui/src/docking_system/splitter_anchor.cpp +++ b/plugins/gui/src/docking_system/splitter_anchor.cpp @@ -95,7 +95,7 @@ namespace hal mDockBar->uncheckButton(widget); } - int SplitterAnchor::count() + int SplitterAnchor::count() const { return mDockBar->count(); } diff --git a/plugins/gui/src/docking_system/tab_widget.cpp b/plugins/gui/src/docking_system/tab_widget.cpp index 42fb55b218d..1934b53b9ef 100644 --- a/plugins/gui/src/docking_system/tab_widget.cpp +++ b/plugins/gui/src/docking_system/tab_widget.cpp @@ -202,7 +202,7 @@ namespace hal hide(); } - int TabWidget::widgetCount() const + int TabWidget::count() const { return mDockBar->count(); } From 656afa0be54b4ed05d3af762757294eb61a7be01 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 31 Jan 2024 21:20:24 +0100 Subject: [PATCH 54/66] Change wrong context index numbers to eliminate warnings --- plugins/gui/src/content_manager/content_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/gui/src/content_manager/content_manager.cpp b/plugins/gui/src/content_manager/content_manager.cpp index 79f1e0e65b0..09fd59069b2 100644 --- a/plugins/gui/src/content_manager/content_manager.cpp +++ b/plugins/gui/src/content_manager/content_manager.cpp @@ -150,7 +150,7 @@ namespace hal void ContentManager::handleOpenDocument(const QString& fileName) { - mExternalIndex = 6; + mExternalIndex = 1; mGraphTabWidget = new GraphTabWidget(); mMainWindow->addContent(mGraphTabWidget, 0, content_anchor::center); @@ -200,7 +200,7 @@ namespace hal mPythonWidget->open(); mPythonConsoleWidget = new PythonConsoleWidget(); - mMainWindow->addContent(mPythonConsoleWidget, 5, content_anchor::bottom); + mMainWindow->addContent(mPythonConsoleWidget, 2, content_anchor::bottom); mPythonConsoleWidget->open(); mContent.append(mGraphTabWidget); From 16ce2551cadab7c8269492c7ab90329d39da1d70 Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 5 Feb 2024 15:45:15 +0100 Subject: [PATCH 55/66] intermediate bugfix --- .../include/gui/user_action/action_pingroup.h | 1 + .../module_ports_tree.cpp | 5 +- .../gui/src/user_action/action_pingroup.cpp | 54 +++++++++++++++++++ plugins/verilog_parser/src/verilog_parser.cpp | 5 ++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/plugins/gui/include/gui/user_action/action_pingroup.h b/plugins/gui/include/gui/user_action/action_pingroup.h index e9b31c54f1f..4ab871068f7 100644 --- a/plugins/gui/include/gui/user_action/action_pingroup.h +++ b/plugins/gui/include/gui/user_action/action_pingroup.h @@ -160,6 +160,7 @@ namespace hal static ActionPingroup* addPinsToNewGroup(const Module* m, const QString& name, QList pinIds, int grpRow = -1); static ActionPingroup* addPinToNewGroup(const Module* m, const QString& name, u32 pinId, int grpRow = -1); static ActionPingroup* removePinsFromGroup(const Module* m, QList pinIds); + static ActionPingroup* deletePinGroup(const Module* m, u32 grpId); }; /** diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp index 366b8fb1d09..b38eea359f7 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/module_ports_tree.cpp @@ -161,9 +161,8 @@ namespace hal auto* pinGroup = mod->get_pin_group_by_id(itemId); if (pinGroup != nullptr) { - ActionPingroup* act = new ActionPingroup(PinActionType::GroupDelete,(u32)itemId); - act->setObject(UserActionObject(mod->get_id(), UserActionObjectType::Module)); - act->exec(); + ActionPingroup* act = ActionPingroup::deletePinGroup(mod,itemId); + if (act) act->exec(); } }); diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index ceeaa40ad2a..0c98041eed0 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -482,6 +482,60 @@ namespace hal return addPinsToNewGroup(m,name,pinIds, grpRow); } + ActionPingroup* ActionPingroup::deletePinGroup(const Module *m, u32 grpId) + { + ActionPingroup* retval = nullptr; + + PinGroup* groupToDelete = m->get_pin_group_by_id(grpId); + if (!groupToDelete) return retval; + + QMap existingGroups; + for (PinGroup* pgroup : m->get_pin_groups()) + existingGroups.insert(QString::fromStdString(pgroup->get_name()),pgroup->get_id()); + + bool doNotDelete = false; // if there is a pin with the same name as the + int vid = -1; + + for (ModulePin* pin : groupToDelete->get_pins()) + { + if (pin->get_name() == groupToDelete->get_name()) + doNotDelete = true; + else + { + QString pinName = QString::fromStdString(pin->get_name()); + auto it = existingGroups.find(pinName); + if (it == existingGroups.end()) + { + if (retval) + retval->mPinActions.append(AtomicAction(PinActionType::GroupCreate,vid,pinName)); + else + retval = new ActionPingroup(PinActionType::GroupCreate,vid,pinName); + retval->mPinActions.append(AtomicAction(PinActionType::PinAsignToGroup,pin->get_id(),"",vid)); + --vid; + } + else + { + if (retval) + retval->mPinActions.append(AtomicAction(PinActionType::PinAsignToGroup,pin->get_id(),"",it.value())); + else + retval = new ActionPingroup(PinActionType::PinAsignToGroup,pin->get_id(),"",it.value()); + } + } + + } + + if (!doNotDelete) + { + if (retval) + retval->mPinActions.append(AtomicAction(PinActionType::GroupDelete,groupToDelete->get_id())); + else + retval = new ActionPingroup(PinActionType::GroupDelete,groupToDelete->get_id()); + } + + retval->setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); + return retval; + } + ActionPingroup* ActionPingroup::removePinsFromGroup(const Module* m, QList pinIds) { ActionPingroup* retval = nullptr; diff --git a/plugins/verilog_parser/src/verilog_parser.cpp b/plugins/verilog_parser/src/verilog_parser.cpp index 985326f2d40..1771503cdba 100644 --- a/plugins/verilog_parser/src/verilog_parser.cpp +++ b/plugins/verilog_parser/src/verilog_parser.cpp @@ -1947,6 +1947,11 @@ namespace hal return {(u32)std::stoi(stream.consume().string)}; } + if (stream.peek(0).string=="35" && stream.peek(2).string=="32") + { + std::cerr << "parser <" << stream.peek(-1).string << ">" << std::endl; + } + // MSB to LSB const int end = std::stoi(stream.consume().string); stream.consume(":", true); From 42a97dc2eefff3e1abf4dc164bb1a2063665b429 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 7 Feb 2024 18:53:41 +0100 Subject: [PATCH 56/66] Bugfix in get_unique_alias(): verify expanded name does not yet exist --- plugins/verilog_parser/src/verilog_parser.cpp | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/plugins/verilog_parser/src/verilog_parser.cpp b/plugins/verilog_parser/src/verilog_parser.cpp index 985326f2d40..f1976ca715a 100644 --- a/plugins/verilog_parser/src/verilog_parser.cpp +++ b/plugins/verilog_parser/src/verilog_parser.cpp @@ -1236,6 +1236,7 @@ namespace hal for (const auto& expanded_port_identifier : port->m_expanded_identifiers) { const auto signal_name = get_unique_alias("", expanded_port_identifier + "__GLOBAL_IO__", m_net_name_occurences); + m_net_name_occurences[signal_name]++; Net* global_port_net = m_netlist->create_net(signal_name); if (global_port_net == nullptr) @@ -1590,7 +1591,9 @@ namespace hal { for (const auto& expanded_name : signal->m_expanded_names) { - signal_alias[expanded_name] = get_unique_alias(module->get_name(), expanded_name, m_net_name_occurences); + std::string unique_net_name = get_unique_alias(module->get_name(), expanded_name, m_net_name_occurences); + m_net_name_occurences[unique_net_name]++; + signal_alias[expanded_name] = unique_net_name; // create new net for the signal Net* signal_net = m_netlist->create_net(signal_alias.at(expanded_name)); @@ -1730,7 +1733,6 @@ namespace hal { // create the new gate instance_alias[instance->m_name] = get_unique_alias(module->get_name(), instance->m_name, m_instance_name_occurences); - Gate* new_gate = m_netlist->create_gate(gate_type_it->second, instance_alias.at(instance->m_name)); if (new_gate == nullptr) { @@ -1931,9 +1933,22 @@ namespace hal if (!parent_name.empty()) { // if there is no other instance with that name, we omit the name prefix - if (const auto instance_name_it = name_occurences.find(name); instance_name_it != name_occurences.end() && instance_name_it->second > 1) + + auto instance_name_it = name_occurences.find(name); + + int cnt = 0; + + // it is OK if base name (first loop cnt=0) is already in name_occurences once + // unique_alias (cnt > 0) must not be in name_occurences + while (instance_name_it != name_occurences.end() && (cnt || instance_name_it->second > 1) ) { - unique_alias = parent_name + instance_name_seperator + unique_alias; + std::string extension; + if (cnt++) + { + extension = "_u" + std::to_string(cnt); + } + unique_alias = parent_name + instance_name_seperator + unique_alias + extension; + instance_name_it = name_occurences.find(unique_alias); } } From 8d4188b704520911f3ce575024d67ff1a5dcd16c Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 7 Feb 2024 19:24:51 +0100 Subject: [PATCH 57/66] Reduce separated net label to essential part following '/' --- .../gui/src/graph_widget/items/nets/labeled_separated_net.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/gui/src/graph_widget/items/nets/labeled_separated_net.cpp b/plugins/gui/src/graph_widget/items/nets/labeled_separated_net.cpp index f21fd6c73be..b1edadd74f0 100644 --- a/plugins/gui/src/graph_widget/items/nets/labeled_separated_net.cpp +++ b/plugins/gui/src/graph_widget/items/nets/labeled_separated_net.cpp @@ -32,6 +32,9 @@ namespace hal LabeledSeparatedNet::LabeledSeparatedNet(Net* n, const QString& text) : SeparatedGraphicsNet(n), mText(text) { + int ipos = mText.lastIndexOf('/'); + if (ipos >= 0) + mText = mText.mid(ipos+1); QFontMetricsF fm(sFont); mTextWidth = fm.width(mText); } From 805b64fe4867a8332f1f8fcebb7c2d0f8b3ba442 Mon Sep 17 00:00:00 2001 From: joern274 Date: Thu, 8 Feb 2024 08:48:21 +0100 Subject: [PATCH 58/66] minor bugfix of bugfix --- plugins/verilog_parser/src/verilog_parser.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/verilog_parser/src/verilog_parser.cpp b/plugins/verilog_parser/src/verilog_parser.cpp index f1976ca715a..216250f66e4 100644 --- a/plugins/verilog_parser/src/verilog_parser.cpp +++ b/plugins/verilog_parser/src/verilog_parser.cpp @@ -1592,7 +1592,8 @@ namespace hal for (const auto& expanded_name : signal->m_expanded_names) { std::string unique_net_name = get_unique_alias(module->get_name(), expanded_name, m_net_name_occurences); - m_net_name_occurences[unique_net_name]++; + if (unique_net_name != expanded_name) + m_net_name_occurences[unique_net_name]++; signal_alias[expanded_name] = unique_net_name; // create new net for the signal From 84ec39bcc8088e76144869a2c315331b84c99442 Mon Sep 17 00:00:00 2001 From: joern274 Date: Thu, 8 Feb 2024 16:58:15 +0100 Subject: [PATCH 59/66] Restore verilog parser after abusing branch for parser test --- plugins/verilog_parser/src/verilog_parser.cpp | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/plugins/verilog_parser/src/verilog_parser.cpp b/plugins/verilog_parser/src/verilog_parser.cpp index 1771503cdba..216250f66e4 100644 --- a/plugins/verilog_parser/src/verilog_parser.cpp +++ b/plugins/verilog_parser/src/verilog_parser.cpp @@ -1236,6 +1236,7 @@ namespace hal for (const auto& expanded_port_identifier : port->m_expanded_identifiers) { const auto signal_name = get_unique_alias("", expanded_port_identifier + "__GLOBAL_IO__", m_net_name_occurences); + m_net_name_occurences[signal_name]++; Net* global_port_net = m_netlist->create_net(signal_name); if (global_port_net == nullptr) @@ -1590,7 +1591,10 @@ namespace hal { for (const auto& expanded_name : signal->m_expanded_names) { - signal_alias[expanded_name] = get_unique_alias(module->get_name(), expanded_name, m_net_name_occurences); + std::string unique_net_name = get_unique_alias(module->get_name(), expanded_name, m_net_name_occurences); + if (unique_net_name != expanded_name) + m_net_name_occurences[unique_net_name]++; + signal_alias[expanded_name] = unique_net_name; // create new net for the signal Net* signal_net = m_netlist->create_net(signal_alias.at(expanded_name)); @@ -1730,7 +1734,6 @@ namespace hal { // create the new gate instance_alias[instance->m_name] = get_unique_alias(module->get_name(), instance->m_name, m_instance_name_occurences); - Gate* new_gate = m_netlist->create_gate(gate_type_it->second, instance_alias.at(instance->m_name)); if (new_gate == nullptr) { @@ -1931,9 +1934,22 @@ namespace hal if (!parent_name.empty()) { // if there is no other instance with that name, we omit the name prefix - if (const auto instance_name_it = name_occurences.find(name); instance_name_it != name_occurences.end() && instance_name_it->second > 1) + + auto instance_name_it = name_occurences.find(name); + + int cnt = 0; + + // it is OK if base name (first loop cnt=0) is already in name_occurences once + // unique_alias (cnt > 0) must not be in name_occurences + while (instance_name_it != name_occurences.end() && (cnt || instance_name_it->second > 1) ) { - unique_alias = parent_name + instance_name_seperator + unique_alias; + std::string extension; + if (cnt++) + { + extension = "_u" + std::to_string(cnt); + } + unique_alias = parent_name + instance_name_seperator + unique_alias + extension; + instance_name_it = name_occurences.find(unique_alias); } } @@ -1947,11 +1963,6 @@ namespace hal return {(u32)std::stoi(stream.consume().string)}; } - if (stream.peek(0).string=="35" && stream.peek(2).string=="32") - { - std::cerr << "parser <" << stream.peek(-1).string << ">" << std::endl; - } - // MSB to LSB const int end = std::stoi(stream.consume().string); stream.consume(":", true); From d192f8817cf04289a3a48a81db479f7a93a10243 Mon Sep 17 00:00:00 2001 From: HerrKermet Date: Thu, 8 Feb 2024 19:45:13 +0100 Subject: [PATCH 60/66] added nullptr checks --- plugins/gui/src/module_widget/module_widget.cpp | 8 ++++++-- .../selection_details_widget.cpp | 10 +++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/plugins/gui/src/module_widget/module_widget.cpp b/plugins/gui/src/module_widget/module_widget.cpp index 0ecaa5212b2..4ec4b21c8b9 100644 --- a/plugins/gui/src/module_widget/module_widget.cpp +++ b/plugins/gui/src/module_widget/module_widget.cpp @@ -611,8 +611,12 @@ namespace hal for (auto module_id : gSelectionRelay->selectedModulesList()) { - QModelIndex index = mModuleProxyModel->mapFromSource(mModuleModel->getIndexFromItem(mModuleModel->getItem(module_id))); - module_selection.select(index, index); + ModuleItem* item = mModuleModel->getItem(module_id); + if(item) + { + QModelIndex index = mModuleProxyModel->mapFromSource(mModuleModel->getIndexFromItem(item)); + module_selection.select(index, index); + } } mTreeView->selectionModel()->select(module_selection, QItemSelectionModel::SelectionFlag::ClearAndSelect); diff --git a/plugins/gui/src/selection_details_widget/selection_details_widget.cpp b/plugins/gui/src/selection_details_widget/selection_details_widget.cpp index 9e9f47f552a..f872c443965 100644 --- a/plugins/gui/src/selection_details_widget/selection_details_widget.cpp +++ b/plugins/gui/src/selection_details_widget/selection_details_widget.cpp @@ -451,10 +451,14 @@ namespace hal // set_name("Selection Details"); break; case SelectionTreeItem::ModuleItem: - mModuleDetailsTabs->setModule(gNetlist->get_module_by_id(sti->id())); - mStackedWidget->setCurrentWidget(mModuleDetailsTabs); -// if (mNumberSelectedItems==1) set_name("Module Details"); + if (Module* m = gNetlist->get_module_by_id(sti->id()); m) + { + mModuleDetailsTabs->setModule(m); + mStackedWidget->setCurrentWidget(mModuleDetailsTabs); + } + // if (mNumberSelectedItems==1) set_name("Module Details"); break; + case SelectionTreeItem::GateItem: if(mStackedWidget->currentWidget() == mModuleDetailsTabs) mModuleDetailsTabs->clear(); From 9dcb3598956dfb97e1c62cfee0cf21cd91861c76 Mon Sep 17 00:00:00 2001 From: joern274 Date: Sat, 10 Feb 2024 18:43:09 +0100 Subject: [PATCH 61/66] several bugfixes related to new multi-drag feature --- .../gui/graph_widget/drag_controller.h | 6 ++++ .../include/gui/graph_widget/graphics_scene.h | 8 +++++ .../items/utility_items/node_drag_shadow.h | 5 ++- .../gui/src/graph_widget/drag_controller.cpp | 31 ++++++++++++++++--- .../gui/src/graph_widget/graphics_scene.cpp | 25 ++++++++++----- .../items/utility_items/node_drag_shadow.cpp | 3 +- .../selection_details_widget.cpp | 7 +++-- .../tree_navigation/selection_tree_model.cpp | 1 - 8 files changed, 68 insertions(+), 18 deletions(-) diff --git a/plugins/gui/include/gui/graph_widget/drag_controller.h b/plugins/gui/include/gui/graph_widget/drag_controller.h index 99b8bf0d4d8..8914ed99746 100644 --- a/plugins/gui/include/gui/graph_widget/drag_controller.h +++ b/plugins/gui/include/gui/graph_widget/drag_controller.h @@ -76,6 +76,12 @@ namespace hal { * @param gridPos - The new grid position of the primary node */ void move(const QPoint& eventPos, bool wantSwap, const QPoint& gridPos); + + /** + * Remove all painted shadows from graphics scene + */ + void clearShadows(GraphicsScene* sc); + bool hasDragged(const QPoint& eventPos); bool isDropAllowed() const; GridPlacement* finalGridPlacement() const; diff --git a/plugins/gui/include/gui/graph_widget/graphics_scene.h b/plugins/gui/include/gui/graph_widget/graphics_scene.h index 910f1f1e5d4..a9965e749e8 100644 --- a/plugins/gui/include/gui/graph_widget/graphics_scene.h +++ b/plugins/gui/include/gui/graph_widget/graphics_scene.h @@ -44,6 +44,7 @@ namespace hal class GraphicsItem; class GraphicsModule; class GraphicsNet; + class DragController; /** * @ingroup graph @@ -275,6 +276,12 @@ namespace hal */ void updateAllItems(); + /** + * Set reference pointer to drag controller on start drag, nullptr when drag ended + * @param dc - Reference to drag controller + */ + void setDragController(DragController* dc); + protected: /** * Handles the mouse event. Used to intercept and ignore right-clicks. @@ -310,6 +317,7 @@ namespace hal qreal mDebugDefaultWidth; qreal mDebugDefaultHeight; bool mDebugGridEnable; + DragController* mDragController; enum RubberBandSelectionStatus { NotPressed, diff --git a/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h b/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h index 8189ddc98b4..8031ce7bd77 100644 --- a/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h +++ b/plugins/gui/include/gui/graph_widget/items/utility_items/node_drag_shadow.h @@ -25,7 +25,7 @@ #pragma once -#include +#include #include #include @@ -35,9 +35,8 @@ namespace hal * @ingroup graph-visuals * @brief An item that is drawn when a node is dragged through the scene. */ - class NodeDragShadow : public QGraphicsObject + class NodeDragShadow : public QGraphicsItem { - Q_OBJECT public: enum class DragCue diff --git a/plugins/gui/src/graph_widget/drag_controller.cpp b/plugins/gui/src/graph_widget/drag_controller.cpp index 361842a2739..d84e1a493f2 100644 --- a/plugins/gui/src/graph_widget/drag_controller.cpp +++ b/plugins/gui/src/graph_widget/drag_controller.cpp @@ -3,6 +3,7 @@ #include "gui/graph_widget/contexts/graph_context.h" #include "gui/graph_widget/graphics_scene.h" #include +#include namespace hal { DragController::DragController(GraphWidget* gw, QObject *parent) @@ -16,6 +17,11 @@ namespace hal { mDropAllowed = false; mWantSwap = false; GraphicsScene* sc = mGraphWidget->getContext()->getLayouter()->scene(); + clearShadows(sc); + } + + void DragController::clearShadows(GraphicsScene *sc) + { if (sc && sc == mShadowScene) { // otherwise (if old scene deleted) items owned by scene already removed @@ -24,8 +30,10 @@ namespace hal { sc->removeItem(nds); delete nds; } + mShadowScene->setDragController(nullptr); } mShadows.clear(); + mShadowScene = nullptr; } NodeDragShadow::DragCue DragController::dragCue() const @@ -44,22 +52,36 @@ namespace hal { // TODO: swap modifier -> deselect all but current QSet nodesToMove; + QSet selGats = gSelectionRelay->selectedGates(); + QSet selMods = gSelectionRelay->selectedModules(); + bool isAlreadySelected = false; + switch (drgItem->itemType()) { case ItemType::Module: nodesToMove.insert(Node(drgItem->id(),Node::Module)); + if (selMods.contains(drgItem->id())) isAlreadySelected = true; break; case ItemType::Gate: nodesToMove.insert(Node(drgItem->id(),Node::Gate)); + if (selGats.contains(drgItem->id())) isAlreadySelected = true; break; default: break; } - for (u32 mid : gSelectionRelay->selectedModules()) - nodesToMove.insert(Node(mid,Node::Module)); - for (u32 gid : gSelectionRelay->selectedGates()) - nodesToMove.insert(Node(gid,Node::Gate)); + if (isAlreadySelected) + { + // multi-select requires that drag node was already selected before + for (u32 mid : selMods) + { + nodesToMove.insert(Node(mid,Node::Module)); + } + for (u32 gid : selGats) + { + nodesToMove.insert(Node(gid,Node::Gate)); + } + } auto context = mGraphWidget->getContext(); const GraphLayouter* layouter = context->getLayouter(); @@ -112,6 +134,7 @@ namespace hal { mShadowScene = mGraphWidget->getContext()->getLayouter()->scene(); if (mShadowScene) { + mShadowScene->setDragController(this); mShadowScene->addItem(nds); mShadows.insert(nb,nds); } diff --git a/plugins/gui/src/graph_widget/graphics_scene.cpp b/plugins/gui/src/graph_widget/graphics_scene.cpp index 9939e56e1db..1525a2a7fa1 100644 --- a/plugins/gui/src/graph_widget/graphics_scene.cpp +++ b/plugins/gui/src/graph_widget/graphics_scene.cpp @@ -7,6 +7,7 @@ #include "gui/graph_widget/graph_widget_constants.h" #include "gui/graph_widget/graphics_factory.h" +#include "gui/graph_widget/drag_controller.h" #include "gui/graph_widget/items/nodes/gates/graphics_gate.h" #include "gui/graph_widget/items/graphics_item.h" #include "gui/graph_widget/items/nodes/modules/graphics_module.h" @@ -75,12 +76,12 @@ namespace hal GraphicsScene::GraphicsScene(QObject* parent) : QGraphicsScene(parent), mDebugGridEnable(false), + mDragController(nullptr), mSelectionStatus(NotPressed) { // FIND OUT IF MANUAL CHANGE TO DEPTH IS NECESSARY / INCREASES PERFORMANCE //mScene.setBspTreeDepth(10); - gSelectionRelay->registerSender(this, "GraphView"); connectAll(); @@ -90,6 +91,8 @@ namespace hal GraphicsScene::~GraphicsScene() { disconnect(this, &QGraphicsScene::selectionChanged, this, &GraphicsScene::handleInternSelectionChanged); + if (mDragController) mDragController->clearShadows(this); + for (QGraphicsItem* gi : items()) { removeItem(gi); @@ -97,6 +100,11 @@ namespace hal } } + void GraphicsScene::setDragController(DragController* dc) + { + mDragController = dc; + } + void GraphicsScene::addGraphItem(GraphicsItem* item) { // SELECTION HAS TO BE UPDATED MANUALLY AFTER ADDING / REMOVING ITEMS @@ -307,10 +315,8 @@ namespace hal void GraphicsScene::deleteAllItems() { - // this breaks the mDragShadowGate - // clear(); - // so we do this instead - // TODO check performance hit + if (mDragController) mDragController->clearShadows(this); + for (auto item : items()) { removeItem(item); @@ -349,10 +355,14 @@ namespace hal void GraphicsScene::setMousePressed(bool isPressed) { if (isPressed) - mSelectionStatus = BeginPressed; + { + // internal selection changed event might fire before mouse pressed event + if (mSelectionStatus != SelectionChanged) + mSelectionStatus = BeginPressed; + } else { - // not pressed ... + // released ... if (mSelectionStatus == SelectionChanged) { mSelectionStatus = EndPressed; @@ -362,6 +372,7 @@ namespace hal } } + void GraphicsScene::handleInternSelectionChanged() { switch (mSelectionStatus) diff --git a/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp b/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp index baddb22012b..63ec05fa826 100644 --- a/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp +++ b/plugins/gui/src/graph_widget/items/utility_items/node_drag_shadow.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace hal { @@ -37,7 +38,7 @@ namespace hal } NodeDragShadow::NodeDragShadow() - : QGraphicsObject(), mRect(0,0,100,100) + : QGraphicsItem(), mRect(0,0,100,100) { setAcceptedMouseButtons(0); } diff --git a/plugins/gui/src/selection_details_widget/selection_details_widget.cpp b/plugins/gui/src/selection_details_widget/selection_details_widget.cpp index 9e9f47f552a..136c2636ed2 100644 --- a/plugins/gui/src/selection_details_widget/selection_details_widget.cpp +++ b/plugins/gui/src/selection_details_widget/selection_details_widget.cpp @@ -451,8 +451,11 @@ namespace hal // set_name("Selection Details"); break; case SelectionTreeItem::ModuleItem: - mModuleDetailsTabs->setModule(gNetlist->get_module_by_id(sti->id())); - mStackedWidget->setCurrentWidget(mModuleDetailsTabs); + if (Module* m = gNetlist->get_module_by_id(sti->id()); m) + { + mModuleDetailsTabs->setModule(gNetlist->get_module_by_id(sti->id())); + mStackedWidget->setCurrentWidget(mModuleDetailsTabs); + } // if (mNumberSelectedItems==1) set_name("Module Details"); break; case SelectionTreeItem::GateItem: diff --git a/plugins/gui/src/selection_details_widget/tree_navigation/selection_tree_model.cpp b/plugins/gui/src/selection_details_widget/tree_navigation/selection_tree_model.cpp index 1cf3bb4b28f..03772c02da5 100644 --- a/plugins/gui/src/selection_details_widget/tree_navigation/selection_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/tree_navigation/selection_tree_model.cpp @@ -191,7 +191,6 @@ namespace hal } } } - beginResetModel(); ++mDoNotDisturb; From 09f359d9e223f1fb71f755a3c17b4b1e00652e51 Mon Sep 17 00:00:00 2001 From: joern274 Date: Sat, 10 Feb 2024 18:47:33 +0100 Subject: [PATCH 62/66] Mentioned multi-drag feature in CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7ffbdac474..ad3ccb7183b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file. * removed layouter code used prior to version 3.1.0 - thus removing the setting option to use that code * added setting option to dump junction layout input data for experts to debug in case of layout errors * miscellaneous + * added drag'n drop feature allowing to move several nodes in graph view at same time * added functions to Python GUI API to create, modifiy and delete views * added GUI PluginParameter type `ComboBox` for parameters that can be requested from plugin * added GUI PluginParameter types `Module` and `Gated` for parameters that can be requested from plugin From 15d2420922304745cc87821532288edafc7ff4f9 Mon Sep 17 00:00:00 2001 From: joern274 Date: Thu, 15 Feb 2024 17:17:49 +0100 Subject: [PATCH 63/66] Several fixes to pin event handling --- include/hal_core/netlist/module.h | 1 - .../include/gui/netlist_relay/netlist_relay.h | 1 + plugins/gui/src/netlist_relay/netlist_relay.cpp | 9 +++++++++ .../module_details_widget/port_tree_model.cpp | 10 ++++++++++ plugins/gui/src/user_action/action_pingroup.cpp | 2 ++ src/netlist/event_system/event_log.cpp | 5 ++++- src/netlist/module.cpp | 17 +++++++++++++---- 7 files changed, 39 insertions(+), 6 deletions(-) diff --git a/include/hal_core/netlist/module.h b/include/hal_core/netlist/module.h index bd4b833553a..0cccca0441e 100644 --- a/include/hal_core/netlist/module.h +++ b/include/hal_core/netlist/module.h @@ -734,7 +734,6 @@ namespace hal std::unordered_map*> m_pin_groups_map; std::unordered_map*> m_pin_group_names_map; std::list*> m_pin_groups_ordered; - u32 m_pin_number[4] = { 0, 0, 0, 0 }; /* stores gates sorted by id */ std::unordered_map m_gates_map; diff --git a/plugins/gui/include/gui/netlist_relay/netlist_relay.h b/plugins/gui/include/gui/netlist_relay/netlist_relay.h index e8cc5c3275e..7e0630d1243 100644 --- a/plugins/gui/include/gui/netlist_relay/netlist_relay.h +++ b/plugins/gui/include/gui/netlist_relay/netlist_relay.h @@ -607,6 +607,7 @@ namespace hal void relayGateEvent(GateEvent::event ev, Gate* gat, u32 associated_data); void relayNetEvent(NetEvent::event ev, Net* net, u32 associated_data); void relayGroupingEvent(GroupingEvent::event ev, Grouping* grp, u32 associated_data); + static void dumpModuleRecursion(Module* m); void handleNetlistModified(); bool mNotified; diff --git a/plugins/gui/src/netlist_relay/netlist_relay.cpp b/plugins/gui/src/netlist_relay/netlist_relay.cpp index 541d46e2655..51c154a50d7 100644 --- a/plugins/gui/src/netlist_relay/netlist_relay.cpp +++ b/plugins/gui/src/netlist_relay/netlist_relay.cpp @@ -661,6 +661,15 @@ namespace hal } } + void NetlistRelay::dumpModuleRecursion(Module *m) + { + for (int i=0; iget_submodule_depth(); i++) + std::cerr << " "; + std::cerr << "Mod " << m->get_id() << " <" << m->get_name() << ">\n"; + for (Module* sm : m->get_submodules()) + dumpModuleRecursion(sm); + } + void NetlistRelay::debugHandleFileOpened() { for (Module* m : gNetlist->get_modules()) diff --git a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp index 204f9191f53..f38d4ad5819 100644 --- a/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp +++ b/plugins/gui/src/selection_details_widget/module_details_widget/port_tree_model.cpp @@ -529,6 +529,11 @@ namespace hal { // group event ptiGroup = mIdToGroupItem.value(pgid); + if (pev != PinEvent::GroupCreate && !ptiGroup) + { + log_warning("gui", "Cannot handle event for pin group ID={}, tree item does not exist.", pgid); + return; + } if (pev != PinEvent::GroupDelete) { pgroup = m->get_pin_group_by_id(pgid); @@ -543,6 +548,11 @@ namespace hal { // pin event ptiPin = mIdToPinItem.value(pgid); + if (pev != PinEvent::PinCreate && !ptiPin) + { + log_warning("gui", "Cannot handle event for pin ID={}, tree item does not exist.", pgid); + return; + } if (pev != PinEvent::PinDelete) { pin = m->get_pin_by_id(pgid); diff --git a/plugins/gui/src/user_action/action_pingroup.cpp b/plugins/gui/src/user_action/action_pingroup.cpp index 0c98041eed0..73de9f426aa 100644 --- a/plugins/gui/src/user_action/action_pingroup.cpp +++ b/plugins/gui/src/user_action/action_pingroup.cpp @@ -3,6 +3,7 @@ #include "hal_core/netlist/grouping.h" #include "gui/grouping/grouping_manager_widget.h" #include "gui/graph_widget/contexts/graph_context.h" +#include "gui/graph_widget/layout_locker.h" #include namespace hal @@ -291,6 +292,7 @@ namespace hal return false; prepareUndoAction(); // create pingroups in case we are going to delete some while assigning + LayoutLocker llock; for (const AtomicAction& aa : mPinActions) { diff --git a/src/netlist/event_system/event_log.cpp b/src/netlist/event_system/event_log.cpp index 1f1973a2603..3dd2eb41a6b 100644 --- a/src/netlist/event_system/event_log.cpp +++ b/src/netlist/event_system/event_log.cpp @@ -287,7 +287,10 @@ namespace hal } else if (event == ModuleEvent::event::pin_changed) { - log_info("event", "changed port of module '{}' (id {:08x})", module->get_name(), module->get_id()); + PinEvent pev = (PinEvent) (associated_data&0xF); + u32 id = (associated_data >> 4); + + log_info("event", "module '{}' (id {:08x}) port event '{}' id={}", module->get_name(), module->get_id(), enum_to_string(pev), id); } else { diff --git a/src/netlist/module.cpp b/src/netlist/module.cpp index 74da03ce416..612db982c25 100644 --- a/src/netlist/module.cpp +++ b/src/netlist/module.cpp @@ -1548,8 +1548,9 @@ namespace hal bool Module::assign_pin_net(const u32 pin_id, Net* net, PinDirection direction) { + PinChangedEventScope scope(this); std::string port_prefix; - + u32 ctr = 0; switch (direction) { case PinDirection::input: @@ -1566,7 +1567,12 @@ namespace hal return false; } - std::string name_internal = port_prefix + "(" + std::to_string(m_pin_number[(int)direction]) + ")"; + std::string name_internal; + do + { + name_internal = port_prefix + "(" + std::to_string(ctr) + ")"; + ctr++; + } while (m_pin_names_map.find(name_internal) != m_pin_names_map.end() || m_pin_group_names_map.find(name_internal) != m_pin_group_names_map.end()); // create pin ModulePin* pin; @@ -1578,6 +1584,7 @@ namespace hal else { pin = res.get(); + PinChangedEvent(this,PinEvent::PinCreate,pin->get_id()).send(); } if (const auto group_res = create_pin_group_internal(get_unique_pin_group_id(), name_internal, pin->get_direction(), pin->get_type(), true, 0, false); group_res.is_error()) @@ -1587,14 +1594,17 @@ namespace hal } else { + PinChangedEvent(this,PinEvent::GroupCreate,group_res.get()->get_id()).send(); if (!group_res.get()->assign_pin(pin)) { log_warning("module", "could not assign pin '{}' to net: failed to assign pin to pin group", name_internal); return false; } + else + PinChangedEvent(this,PinEvent::PinAssignToGroup,pin->get_id()).send(); } - PinChangedEvent(this,PinEvent::GroupCreate,pin->get_group().first->get_id()).send(); + scope.send_events(); return true; } @@ -1682,7 +1692,6 @@ namespace hal m_pins.push_back(std::move(pin_owner)); m_pins_map[id] = pin; m_pin_names_map[name] = pin; - ++m_pin_number[(int)direction]; // mark pin ID as used if (auto free_id_it = m_free_pin_ids.find(id); free_id_it != m_free_pin_ids.end()) From 54dacd6ca67fe504b9d3097bf76f56bf7ded7c64 Mon Sep 17 00:00:00 2001 From: joern274 Date: Mon, 19 Feb 2024 09:02:58 +0100 Subject: [PATCH 64/66] Remember module ID upon 'undo module delete' --- plugins/gui/src/user_action/action_create_object.cpp | 9 ++++++++- plugins/gui/src/user_action/action_delete_object.cpp | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/gui/src/user_action/action_create_object.cpp b/plugins/gui/src/user_action/action_create_object.cpp index 8a501f44b13..236cf48c73e 100644 --- a/plugins/gui/src/user_action/action_create_object.cpp +++ b/plugins/gui/src/user_action/action_create_object.cpp @@ -86,8 +86,15 @@ namespace hal Module* parentModule = gNetlist->get_module_by_id(mParentId); if (parentModule) { - Module* m = gNetlist->create_module(gNetlist->get_unique_module_id(), + u32 modId = mObject.id() ? mObject.id() : gNetlist->get_unique_module_id(); + Module* m = gNetlist->create_module(modId, mObjectName.toStdString(), parentModule); + if (!m) + { + log_warning("gui", "Failed to create module '{}' with ID={} under parent ID={}.", + mObjectName.toStdString(), modId, mParentId); + return false; + } setObject(UserActionObject(m->get_id(),UserActionObjectType::Module)); standardUndo = true; } diff --git a/plugins/gui/src/user_action/action_delete_object.cpp b/plugins/gui/src/user_action/action_delete_object.cpp index 23db22c4cd8..ad1a5c5266e 100644 --- a/plugins/gui/src/user_action/action_delete_object.cpp +++ b/plugins/gui/src/user_action/action_delete_object.cpp @@ -74,6 +74,7 @@ namespace hal UserActionCompound* act = new UserActionCompound; act->setUseCreatedObject(); ActionCreateObject* actCreate = new ActionCreateObject(UserActionObjectType::Module, QString::fromStdString(mod->get_name())); + actCreate->setObject(mObject); actCreate->setParentId(mod->get_parent_module()->get_id()); act->addAction(actCreate); act->addAction(new ActionSetObjectType(QString::fromStdString(mod->get_type()))); From 088ea00b0cfb323037e2de8e78970f309116c4c4 Mon Sep 17 00:00:00 2001 From: joern274 Date: Wed, 21 Feb 2024 16:58:32 +0100 Subject: [PATCH 65/66] Bugfix: lock view layout redraw by ID (not by pointer) --- .../gui/include/gui/graph_widget/layout_locker.h | 3 ++- plugins/gui/src/graph_widget/layout_locker.cpp | 14 ++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/plugins/gui/include/gui/graph_widget/layout_locker.h b/plugins/gui/include/gui/graph_widget/layout_locker.h index ebc6472a05c..34af29617d5 100644 --- a/plugins/gui/include/gui/graph_widget/layout_locker.h +++ b/plugins/gui/include/gui/graph_widget/layout_locker.h @@ -26,6 +26,7 @@ #pragma once #include +#include "hal_core/defines.h" namespace hal { @@ -37,7 +38,7 @@ namespace hal LayoutLockerManager(); int mLockCount; - QSet mWaitingRoom; + QSet mWaitingRoom; public: static LayoutLockerManager* instance(); diff --git a/plugins/gui/src/graph_widget/layout_locker.cpp b/plugins/gui/src/graph_widget/layout_locker.cpp index 70082902bdb..e2bf1f46bb2 100644 --- a/plugins/gui/src/graph_widget/layout_locker.cpp +++ b/plugins/gui/src/graph_widget/layout_locker.cpp @@ -1,4 +1,5 @@ #include "gui/graph_widget/layout_locker.h" +#include "gui/gui_globals.h" #include "gui/graph_widget/contexts/graph_context.h" namespace hal { @@ -22,24 +23,29 @@ namespace hal { void LayoutLockerManager::removeLock() { --mLockCount; + if (mLockCount <= 0 && !mWaitingRoom.isEmpty()) { - for (GraphContext* ctx : mWaitingRoom) - ctx->startSceneUpdate(); + for (u32 ctxId : mWaitingRoom) + { + GraphContext* ctx = gGraphContextManager->getContextById(ctxId); + if (ctx) ctx->startSceneUpdate(); + } mWaitingRoom.clear(); } } bool LayoutLockerManager::canUpdate(GraphContext* ctx) { + if (!ctx) return false; if (mLockCount <= 0) return true; - mWaitingRoom.insert(ctx); + mWaitingRoom.insert(ctx->id()); return false; } void LayoutLockerManager::removeWaitingContext(GraphContext* ctx) { - auto it = mWaitingRoom.find(ctx); + auto it = mWaitingRoom.find(ctx->id()); if (it != mWaitingRoom.end()) mWaitingRoom.erase(it); } From 62b5f7cfd52502bf8eafa7f9cf4bbf67a9062338 Mon Sep 17 00:00:00 2001 From: joern274 Date: Thu, 29 Feb 2024 13:26:27 +0100 Subject: [PATCH 66/66] Added INIT field declaration to FF-gate-types in example library --- CHANGELOG.md | 1 + .../definitions/example_library.hgl | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad3ccb7183b..ac0e1def95b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file. * removed layouter code used prior to version 3.1.0 - thus removing the setting option to use that code * added setting option to dump junction layout input data for experts to debug in case of layout errors * miscellaneous + * added INIT field declaration to FF-gate-types in example library * added drag'n drop feature allowing to move several nodes in graph view at same time * added functions to Python GUI API to create, modifiy and delete views * added GUI PluginParameter type `ComboBox` for parameters that can be requested from plugin diff --git a/plugins/gate_libraries/definitions/example_library.hgl b/plugins/gate_libraries/definitions/example_library.hgl index b42016e1c2c..ed1af26c8af 100644 --- a/plugins/gate_libraries/definitions/example_library.hgl +++ b/plugins/gate_libraries/definitions/example_library.hgl @@ -2055,7 +2055,9 @@ "state": "IQ", "neg_state": "IQN", "next_state": "(D & CE)", - "clocked_on": "C" + "clocked_on": "C", + "data_category": "generic", + "data_identifier": "INIT" }, "pin_groups": [ { @@ -2128,7 +2130,9 @@ "neg_state": "IQN", "next_state": "(D & CE)", "clocked_on": "C", - "preset_on": "S" + "preset_on": "S", + "data_category": "generic", + "data_identifier": "INIT" }, "pin_groups": [ { @@ -2215,7 +2219,9 @@ "neg_state": "IQN", "next_state": "(D & CE)", "clocked_on": "C", - "clear_on": "R" + "clear_on": "R", + "data_category": "generic", + "data_identifier": "INIT" }, "pin_groups": [ { @@ -2305,7 +2311,9 @@ "clear_on": "R", "preset_on": "S", "state_clear_preset": "L", - "neg_state_clear_preset": "H" + "neg_state_clear_preset": "H", + "data_category": "generic", + "data_identifier": "INIT" }, "pin_groups": [ { @@ -2444,4 +2452,4 @@ ] } ] -} \ No newline at end of file +}