Implement standard-library-compatible iterators for Dictionary

This commit is contained in:
Tim Wojtulewicz 2020-09-22 13:59:13 -07:00
parent 9e9998c6e5
commit 892124378c
16 changed files with 834 additions and 254 deletions

View file

@ -495,13 +495,15 @@ static void BuildJSON(threading::formatter::JSON::NullDoubleWriter& writer, Val*
else
writer.StartObject();
detail::HashKey* k;
std::unique_ptr<detail::HashKey> k;
TableEntryVal* entry;
auto c = table->InitForIteration();
while ( (entry = table->NextEntry(k, c)) )
for ( const auto& te : *table )
{
entry = te.GetValue<TableEntryVal*>();
k = te.GetHashKey();
auto lv = tval->RecreateIndex(*k);
delete k;
Val* entry_key = lv->Length() == 1 ? lv->Idx(0).get() : lv.get();
if ( tval->GetType()->IsSet() )
@ -1438,7 +1440,7 @@ void TableVal::Init(TableTypePtr t)
table_type = std::move(t);
expire_func = nullptr;
expire_time = nullptr;
expire_cookie = nullptr;
expire_iterator = nullptr;
timer = nullptr;
def_val = nullptr;
@ -1460,6 +1462,7 @@ TableVal::~TableVal()
delete table_hash;
delete table_val;
delete subnets;
delete expire_iterator;
}
void TableVal::RemoveAll()
@ -1483,11 +1486,9 @@ int TableVal::RecursiveSize() const
GetType()->AsTableType()->Yield()->Tag() != TYPE_TABLE )
return n;
IterCookie* c = table_val->InitForIteration();
TableEntryVal* tv;
while ( (tv = table_val->NextEntry(c)) )
for ( const auto& ve : *table_val )
{
auto* tv = ve.GetValue<TableEntryVal*>();
if ( tv->GetVal() )
n += tv->GetVal()->AsTableVal()->RecursiveSize();
}
@ -1657,15 +1658,12 @@ bool TableVal::AddTo(Val* val, bool is_first_init, bool propagate_ops) const
return false;
}
IterCookie* c = table_val->InitForIteration();
detail::HashKey* k;
TableEntryVal* v;
while ( (v = table_val->NextEntry(k, c)) )
for ( const auto& tble : *table_val )
{
std::unique_ptr<detail::HashKey> hk{k};
auto k = tble.GetHashKey();
auto* v = tble.GetValue<TableEntryVal*>();
if ( is_first_init && t->AsTable()->Lookup(k) )
if ( is_first_init && t->AsTable()->Lookup(k.get()) )
{
auto key = table_hash->RecoverVals(*k);
// ### Shouldn't complain if their values are equal.
@ -1675,12 +1673,12 @@ bool TableVal::AddTo(Val* val, bool is_first_init, bool propagate_ops) const
if ( type->IsSet() )
{
if ( ! t->Assign(v->GetVal(), std::move(hk), nullptr) )
if ( ! t->Assign(v->GetVal(), std::move(k), nullptr) )
return false;
}
else
{
if ( ! t->Assign(nullptr, std::move(hk), v->GetVal()) )
if ( ! t->Assign(nullptr, std::move(k), v->GetVal()) )
return false;
}
}
@ -1704,18 +1702,15 @@ bool TableVal::RemoveFrom(Val* val) const
return false;
}
IterCookie* c = table_val->InitForIteration();
detail::HashKey* k;
while ( table_val->NextEntry(k, c) )
for ( const auto& tble : *table_val )
{
// Not sure that this is 100% sound, since the HashKey
// comes from one table but is being used in another.
// OTOH, they are both the same type, so as long as
// we don't have hash keys that are keyed per dictionary,
// it should work ...
auto k = tble.GetHashKey();
t->Remove(*k);
delete k;
}
return true;
@ -1736,16 +1731,15 @@ TableValPtr TableVal::Intersection(const TableVal& tv) const
t0 = tmp;
}
IterCookie* c = t1->InitForIteration();
detail::HashKey* k;
while ( t1->NextEntry(k, c) )
const PDict<TableEntryVal>* tbl = AsTable();
for ( const auto& tble : *tbl )
{
auto k = tble.GetHashKey();
// Here we leverage the same assumption about consistent
// hashes as in TableVal::RemoveFrom above.
if ( t0->Lookup(k) )
result->table_val->Insert(k, new TableEntryVal(nullptr));
delete k;
if ( t0->Lookup(k.get()) )
result->table_val->Insert(k.get(), new TableEntryVal(nullptr));
}
return result;
@ -1759,20 +1753,14 @@ bool TableVal::EqualTo(const TableVal& tv) const
if ( t0->Length() != t1->Length() )
return false;
IterCookie* c = t0->InitForIteration();
detail::HashKey* k;
while ( t0->NextEntry(k, c) )
for ( const auto& tble : *t0 )
{
auto k = tble.GetHashKey();
// Here we leverage the same assumption about consistent
// hashes as in TableVal::RemoveFrom above.
if ( ! t1->Lookup(k) )
{
delete k;
t0->StopIteration(c);
if ( ! t1->Lookup(k.get()) )
return false;
}
delete k;
}
return true;
@ -1786,20 +1774,14 @@ bool TableVal::IsSubsetOf(const TableVal& tv) const
if ( t0->Length() > t1->Length() )
return false;
IterCookie* c = t0->InitForIteration();
detail::HashKey* k;
while ( t0->NextEntry(k, c) )
for ( const auto& tble : *t0 )
{
auto k = tble.GetHashKey();
// Here we leverage the same assumption about consistent
// hashes as in TableVal::RemoveFrom above.
if ( ! t1->Lookup(k) )
{
delete k;
t0->StopIteration(c);
if ( ! t1->Lookup(k.get()) )
return false;
}
delete k;
}
return true;
@ -2331,11 +2313,9 @@ ListValPtr TableVal::ToListVal(TypeTag t) const
{
auto l = make_intrusive<ListVal>(t);
IterCookie* c = table_val->InitForIteration();
detail::HashKey* k;
while ( table_val->NextEntry(k, c) )
for ( const auto& tble : *table_val )
{
auto k = tble.GetHashKey();
auto index = table_hash->RecoverVals(*k);
if ( t == TYPE_ANY )
@ -2348,8 +2328,6 @@ ListValPtr TableVal::ToListVal(TypeTag t) const
l->Append(index->Idx(0));
}
delete k;
}
return l;
@ -2400,16 +2378,16 @@ void TableVal::Describe(ODesc* d) const
d->PushIndent();
}
IterCookie* c = table_val->InitForIteration();
auto iter = table_val->begin();
for ( int i = 0; i < n; ++i )
{
detail::HashKey* k;
TableEntryVal* v = table_val->NextEntry(k, c);
if ( ! v )
if ( iter == table_val->end() )
reporter->InternalError("hash table underflow in TableVal::Describe");
auto k = iter->GetHashKey();
auto* v = iter->GetValue<TableEntryVal*>();
auto vl = table_hash->RecoverVals(*k);
int dim = vl->Length();
@ -2434,8 +2412,6 @@ void TableVal::Describe(ODesc* d) const
vl->Describe(d);
delete k;
if ( table_type->IsSet() )
{ // We're a set, not a table.
if ( d->IsReadable() )
@ -2455,9 +2431,11 @@ void TableVal::Describe(ODesc* d) const
d->Add(" @");
d->Add(util::detail::fmt_access_time(v->ExpireAccessTime()));
}
++iter;
}
if ( table_val->NextEntry(c) )
if ( iter != table_val->end() )
reporter->InternalError("hash table overflow in TableVal::Describe");
if ( d->IsPortable() || d->IsReadable() )
@ -2552,20 +2530,23 @@ void TableVal::DoExpire(double t)
// error, it has been reported already.
return;
if ( ! expire_cookie )
if ( ! expire_iterator )
{
expire_cookie = table_val->InitForIteration();
table_val->MakeRobustCookie(expire_cookie);
auto it = table_val->begin_robust();
expire_iterator = new RobustDictIterator(std::move(it));
}
detail::HashKey* k = nullptr;
std::unique_ptr<detail::HashKey> k = nullptr;
TableEntryVal* v = nullptr;
TableEntryVal* v_saved = nullptr;
bool modified = false;
for ( int i = 0; i < zeek::detail::table_incremental_step &&
(v = table_val->NextEntry(k, expire_cookie)); ++i )
*expire_iterator != table_val->end_robust(); ++i, ++(*expire_iterator) )
{
k = (*expire_iterator)->GetHashKey();
v = (*expire_iterator)->GetValue<TableEntryVal*>();
if ( v->ExpireAccessTime() == 0 )
{
// This happens when we insert val while network_time
@ -2588,12 +2569,11 @@ void TableVal::DoExpire(double t)
// function modified or deleted the table
// value, so look it up again.
v_saved = v;
v = table_val->Lookup(k);
v = table_val->Lookup(k.get());
if ( ! v )
{ // user-provided function deleted it
v = v_saved;
delete k;
continue;
}
@ -2602,7 +2582,6 @@ void TableVal::DoExpire(double t)
// User doesn't want us to expire
// this now.
v->SetExpireAccess(run_state::network_time - timeout + secs);
delete k;
continue;
}
@ -2616,7 +2595,7 @@ void TableVal::DoExpire(double t)
reporter->InternalWarning("index not in prefix table");
}
table_val->RemoveEntry(k);
table_val->RemoveEntry(k.get());
if ( change_func )
{
if ( ! idx )
@ -2628,16 +2607,15 @@ void TableVal::DoExpire(double t)
delete v;
modified = true;
}
delete k;
}
if ( modified )
Modified();
if ( ! v )
if ( (*expire_iterator) == table_val->end_robust() )
{
expire_cookie = nullptr;
delete expire_iterator;
expire_iterator = nullptr;
InitTimer(zeek::detail::table_expire_interval);
}
else
@ -2740,22 +2718,19 @@ ValPtr TableVal::DoClone(CloneState* state)
auto tv = make_intrusive<TableVal>(table_type);
state->NewClone(this, tv);
IterCookie* cookie = table_val->InitForIteration();
detail::HashKey* key;
TableEntryVal* val;
while ( (val = table_val->NextEntry(key, cookie)) )
const PDict<TableEntryVal>* tbl = AsTable();
for ( const auto& tble : *table_val )
{
auto key = tble.GetHashKey();
auto* val = tble.GetValue<TableEntryVal*>();
TableEntryVal* nval = val->Clone(state);
tv->table_val->Insert(key, nval);
tv->table_val->Insert(key.get(), nval);
if ( subnets )
{
auto idx = RecreateIndex(*key);
tv->subnets->Insert(idx.get(), nval);
}
delete key;
}
tv->attrs = attrs;
@ -2783,11 +2758,9 @@ unsigned int TableVal::MemoryAllocation() const
{
unsigned int size = 0;
IterCookie* c = table_val->InitForIteration();
TableEntryVal* tv;
while ( (tv = table_val->NextEntry(c)) )
for ( const auto& ve : *table_val )
{
auto* tv = ve.GetValue<TableEntryVal*>();
if ( tv->GetVal() )
size += tv->GetVal()->MemoryAllocation();
size += padded_sizeof(TableEntryVal);
@ -2833,17 +2806,13 @@ void TableVal::DoneParsing()
TableVal::ParseTimeTableState TableVal::DumpTableState()
{
IterCookie* cookie = table_val->InitForIteration();
detail::HashKey* key;
TableEntryVal* val;
ParseTimeTableState rval;
while ( (val = table_val->NextEntry(key, cookie)) )
for ( const auto& tble : *table_val )
{
auto key = tble.GetHashKey();
auto* val = tble.GetValue<TableEntryVal*>();
rval.emplace_back(RecreateIndex(*key), val->GetVal());
delete key;
}
RemoveAll();