-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ProgramMemory: only copy mValues
on write
#6646
Changes from all commits
28680db
a0829f5
235691e
ce6d87f
3af3159
809b0e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ | |
#include <cstddef> | ||
#include <functional> | ||
#include <map> | ||
#include <memory> | ||
#include <string> | ||
#include <unordered_map> | ||
#include <utility> | ||
|
@@ -96,12 +97,12 @@ struct ExprIdToken { | |
}; | ||
}; | ||
|
||
struct ProgramMemory { | ||
struct CPPCHECKLIB ProgramMemory { | ||
using Map = std::unordered_map<ExprIdToken, ValueFlow::Value, ExprIdToken::Hash>; | ||
|
||
ProgramMemory() = default; | ||
ProgramMemory() : mValues(new Map()) {} | ||
|
||
explicit ProgramMemory(Map values) : mValues(std::move(values)) {} | ||
explicit ProgramMemory(Map values) : mValues(new Map(std::move(values))) {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would construct a |
||
|
||
void setValue(const Token* expr, const ValueFlow::Value& value); | ||
const ValueFlow::Value* getValue(nonneg int exprid, bool impossible = false) const; | ||
|
@@ -131,22 +132,24 @@ struct ProgramMemory { | |
|
||
void replace(ProgramMemory pm); | ||
|
||
void insert(const ProgramMemory &pm); | ||
|
||
Map::iterator begin() { | ||
return mValues.begin(); | ||
copyOnWrite(); | ||
|
||
return mValues->begin(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lacks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
} | ||
|
||
Map::iterator end() { | ||
return mValues.end(); | ||
copyOnWrite(); | ||
|
||
return mValues->end(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lacks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
} | ||
|
||
Map::const_iterator begin() const { | ||
return mValues.begin(); | ||
return mValues->cbegin(); | ||
} | ||
|
||
Map::const_iterator end() const { | ||
return mValues.end(); | ||
return mValues->cend(); | ||
} | ||
|
||
friend bool operator==(const ProgramMemory& x, const ProgramMemory& y) { | ||
|
@@ -158,7 +161,9 @@ struct ProgramMemory { | |
} | ||
|
||
private: | ||
Map mValues; | ||
void copyOnWrite(); | ||
|
||
std::shared_ptr<Map> mValues; | ||
}; | ||
|
||
struct ProgramMemoryState { | ||
|
@@ -168,7 +173,6 @@ struct ProgramMemoryState { | |
|
||
explicit ProgramMemoryState(const Settings* s); | ||
|
||
void insert(const ProgramMemory &pm, const Token* origin = nullptr); | ||
void replace(ProgramMemory pm, const Token* origin = nullptr); | ||
|
||
void addState(const Token* tok, const ProgramMemory::Map& vars); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* Cppcheck - A tool for static C/C++ code analysis | ||
* Copyright (C) 2007-2024 Cppcheck team. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "fixture.h" | ||
#include "helpers.h" | ||
#include "token.h" | ||
#include "programmemory.h" | ||
#include "vfvalue.h" | ||
|
||
class TestProgramMemory : public TestFixture { | ||
public: | ||
TestProgramMemory() : TestFixture("TestProgramMemory") {} | ||
|
||
private: | ||
void run() override { | ||
TEST_CASE(copyOnWrite); | ||
} | ||
|
||
void copyOnWrite() const { | ||
SimpleTokenList tokenlist("1+1;"); | ||
Token* tok = tokenlist.front(); | ||
const nonneg int id = 123; | ||
tok->exprId(id); | ||
|
||
ProgramMemory pm; | ||
const ValueFlow::Value* v = pm.getValue(id); | ||
ASSERT(!v); | ||
pm.setValue(tok, ValueFlow::Value{41}); | ||
|
||
v = pm.getValue(id); | ||
ASSERT(v); | ||
ASSERT_EQUALS(41, v->intvalue); | ||
|
||
// create a copy | ||
ProgramMemory pm2 = pm; | ||
|
||
// make sure the value was copied | ||
v = pm2.getValue(id); | ||
ASSERT(v); | ||
ASSERT_EQUALS(41, v->intvalue); | ||
|
||
// set a value in the copy to trigger copy-on-write | ||
pm2.setValue(tok, ValueFlow::Value{42}); | ||
|
||
// make another copy and set another value | ||
ProgramMemory pm3 = pm2; | ||
|
||
// set a value in the copy to trigger copy-on-write | ||
pm3.setValue(tok, ValueFlow::Value{43}); | ||
|
||
// make sure the value was set | ||
v = pm2.getValue(id); | ||
ASSERT(v); | ||
ASSERT_EQUALS(42, v->intvalue); | ||
|
||
// make sure the value was set | ||
v = pm3.getValue(id); | ||
ASSERT(v); | ||
ASSERT_EQUALS(43, v->intvalue); | ||
|
||
// make sure the original value remains unchanged | ||
v = pm.getValue(id); | ||
ASSERT(v); | ||
ASSERT_EQUALS(41, v->intvalue); | ||
} | ||
}; | ||
|
||
REGISTER_TEST(TestProgramMemory) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this be written as remove-erase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly but that would not fix the TODO and just clean up the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the call to
remove_if()
returnsend()
, no modification takes place, or am I missing something?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we would have to determine if a modification happens, then make the copy and afterwards iterate it again because the existing result is based on a different container.
We only need to make this optimization if it is a hot spot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove_if
wouldn't work here anyway: https://stackoverflow.com/questions/9210014/remove-elements-from-an-unordered-map-fulfilling-a-predicate