Skip to content

Commit 006cdf6

Browse files
committed
Merge #7907: Optimize and Cleanup CScript::FindAndDelete
d1d7775 Improve worst-case behavior of CScript::FindAndDelete (Patrick Strateman) e2a30bc Unit test for CScript::FindAndDelete (Gavin Andresen) c0f660c Replace c-style cast with c++ style static_cast. (Patrick Strateman) ec9ad5f Replace memcmp with std::equal in CScript::FindAndDelete (Patrick Strateman)
2 parents 3b9a0bf + d1d7775 commit 006cdf6

File tree

2 files changed

+129
-3
lines changed

2 files changed

+129
-3
lines changed

src/script/script.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -573,17 +573,26 @@ class CScript : public CScriptBase
573573
int nFound = 0;
574574
if (b.empty())
575575
return nFound;
576-
iterator pc = begin();
576+
CScript result;
577+
iterator pc = begin(), pc2 = begin();
577578
opcodetype opcode;
578579
do
579580
{
580-
while (end() - pc >= (long)b.size() && memcmp(&pc[0], &b[0], b.size()) == 0)
581+
result.insert(result.end(), pc2, pc);
582+
while (static_cast<size_t>(end() - pc) >= b.size() && std::equal(b.begin(), b.end(), pc))
581583
{
582-
pc = erase(pc, pc + b.size());
584+
pc = pc + b.size();
583585
++nFound;
584586
}
587+
pc2 = pc;
585588
}
586589
while (GetOp(pc, opcode));
590+
591+
if (nFound > 0) {
592+
result.insert(result.end(), pc2, end());
593+
*this = result;
594+
}
595+
587596
return nFound;
588597
}
589598
int Find(opcodetype op) const

src/test/script_tests.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,4 +1051,121 @@ BOOST_AUTO_TEST_CASE(script_GetScriptAsm)
10511051
BOOST_CHECK_EQUAL(derSig + "83 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "83")) << vchPubKey));
10521052
}
10531053

1054+
static CScript
1055+
ScriptFromHex(const char* hex)
1056+
{
1057+
std::vector<unsigned char> data = ParseHex(hex);
1058+
return CScript(data.begin(), data.end());
1059+
}
1060+
1061+
1062+
BOOST_AUTO_TEST_CASE(script_FindAndDelete)
1063+
{
1064+
// Exercise the FindAndDelete functionality
1065+
CScript s;
1066+
CScript d;
1067+
CScript expect;
1068+
1069+
s = CScript() << OP_1 << OP_2;
1070+
d = CScript(); // delete nothing should be a no-op
1071+
expect = s;
1072+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
1073+
BOOST_CHECK(s == expect);
1074+
1075+
s = CScript() << OP_1 << OP_2 << OP_3;
1076+
d = CScript() << OP_2;
1077+
expect = CScript() << OP_1 << OP_3;
1078+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1079+
BOOST_CHECK(s == expect);
1080+
1081+
s = CScript() << OP_3 << OP_1 << OP_3 << OP_3 << OP_4 << OP_3;
1082+
d = CScript() << OP_3;
1083+
expect = CScript() << OP_1 << OP_4;
1084+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 4);
1085+
BOOST_CHECK(s == expect);
1086+
1087+
s = ScriptFromHex("0302ff03"); // PUSH 0x02ff03 onto stack
1088+
d = ScriptFromHex("0302ff03");
1089+
expect = CScript();
1090+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1091+
BOOST_CHECK(s == expect);
1092+
1093+
s = ScriptFromHex("0302ff030302ff03"); // PUSH 0x2ff03 PUSH 0x2ff03
1094+
d = ScriptFromHex("0302ff03");
1095+
expect = CScript();
1096+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
1097+
BOOST_CHECK(s == expect);
1098+
1099+
s = ScriptFromHex("0302ff030302ff03");
1100+
d = ScriptFromHex("02");
1101+
expect = s; // FindAndDelete matches entire opcodes
1102+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
1103+
BOOST_CHECK(s == expect);
1104+
1105+
s = ScriptFromHex("0302ff030302ff03");
1106+
d = ScriptFromHex("ff");
1107+
expect = s;
1108+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
1109+
BOOST_CHECK(s == expect);
1110+
1111+
// This is an odd edge case: strip of the push-three-bytes
1112+
// prefix, leaving 02ff03 which is push-two-bytes:
1113+
s = ScriptFromHex("0302ff030302ff03");
1114+
d = ScriptFromHex("03");
1115+
expect = CScript() << ParseHex("ff03") << ParseHex("ff03");
1116+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
1117+
BOOST_CHECK(s == expect);
1118+
1119+
// Byte sequence that spans multiple opcodes:
1120+
s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY
1121+
d = ScriptFromHex("feed51");
1122+
expect = s;
1123+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); // doesn't match 'inside' opcodes
1124+
BOOST_CHECK(s == expect);
1125+
1126+
s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY
1127+
d = ScriptFromHex("02feed51");
1128+
expect = ScriptFromHex("69");
1129+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1130+
BOOST_CHECK(s == expect);
1131+
1132+
s = ScriptFromHex("516902feed5169");
1133+
d = ScriptFromHex("feed51");
1134+
expect = s;
1135+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
1136+
BOOST_CHECK(s == expect);
1137+
1138+
s = ScriptFromHex("516902feed5169");
1139+
d = ScriptFromHex("02feed51");
1140+
expect = ScriptFromHex("516969");
1141+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1142+
BOOST_CHECK(s == expect);
1143+
1144+
s = CScript() << OP_0 << OP_0 << OP_1 << OP_1;
1145+
d = CScript() << OP_0 << OP_1;
1146+
expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass
1147+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1148+
BOOST_CHECK(s == expect);
1149+
1150+
s = CScript() << OP_0 << OP_0 << OP_1 << OP_0 << OP_1 << OP_1;
1151+
d = CScript() << OP_0 << OP_1;
1152+
expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass
1153+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
1154+
BOOST_CHECK(s == expect);
1155+
1156+
// Another weird edge case:
1157+
// End with invalid push (not enough data)...
1158+
s = ScriptFromHex("0003feed");
1159+
d = ScriptFromHex("03feed"); // ... can remove the invalid push
1160+
expect = ScriptFromHex("00");
1161+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1162+
BOOST_CHECK(s == expect);
1163+
1164+
s = ScriptFromHex("0003feed");
1165+
d = ScriptFromHex("00");
1166+
expect = ScriptFromHex("03feed");
1167+
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1168+
BOOST_CHECK(s == expect);
1169+
}
1170+
10541171
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)