diff --git a/src/Val.cc b/src/Val.cc index 0fbbc20979..da287d66b4 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -3177,14 +3177,19 @@ ValPtr TypeVal::DoClone(CloneState* state) return {NewRef{}, this}; } -VectorVal::VectorVal(VectorTypePtr t) : Val(std::move(t)) +VectorVal::VectorVal(VectorTypePtr t) : Val(t) { - vector_val = new vector(); + vector_val = new vector(); + yield_type = t->Yield(); + + auto y_tag = yield_type->Tag(); + any_yield = (y_tag == TYPE_VOID || y_tag == TYPE_ANY); } VectorVal::~VectorVal() { delete vector_val; + delete yield_types; } ValPtr VectorVal::SizeVal() const @@ -3192,16 +3197,64 @@ ValPtr VectorVal::SizeVal() const return val_mgr->Count(uint32_t(vector_val->size())); } +bool VectorVal::CheckElementType(const ValPtr& element) + { + if ( ! element ) + // Insertion isn't actually going to happen. + return true; + + if ( yield_types ) + // We're already a heterogeneous vector-of-any. + return true; + + if ( any_yield ) + { + auto n = vector_val->size(); + + if ( n == 0 ) + // First addition to an empty vector-of-any, perhaps + // it will be homogeneous. + yield_type = element->GetType(); + + else + { + yield_types = new std::vector(); + + // Since we're only now switching to the heterogeneous + // representation, capture the types of the existing + // elements. + + for ( auto i = 0; i < n; ++i ) + yield_types->emplace_back(yield_type); + } + } + + else if ( ! same_type(element->GetType(), yield_type, false) ) + return false; + + return true; + } + bool VectorVal::Assign(unsigned int index, ValPtr element) { - if ( element && - ! same_type(element->GetType(), GetType()->AsVectorType()->Yield(), false) ) + if ( ! CheckElementType(element) ) return false; if ( index >= vector_val->size() ) + { vector_val->resize(index + 1); + if ( yield_types ) + yield_types->resize(index + 1); + } - (*vector_val)[index] = std::move(element); + if ( yield_types ) + { + const auto& t = element->GetType(); + (*yield_types)[index] = t; + (*vector_val)[index] = ZVal(std::move(element), t); + } + else + (*vector_val)[index] = ZVal(std::move(element), yield_type); Modified(); return true; @@ -3221,20 +3274,33 @@ bool VectorVal::AssignRepeat(unsigned int index, unsigned int how_many, bool VectorVal::Insert(unsigned int index, ValPtr element) { - if ( element && - ! same_type(element->GetType(), GetType()->AsVectorType()->Yield(), false) ) - { + if ( ! CheckElementType(element) ) return false; - } - vector::iterator it; + vector::iterator it; + vector::iterator types_it; if ( index < vector_val->size() ) + { it = std::next(vector_val->begin(), index); + if ( yield_types ) + types_it = std::next(yield_types->begin(), index); + } else + { it = vector_val->end(); + if ( yield_types ) + types_it = yield_types->end(); + } - vector_val->insert(it, std::move(element)); + if ( yield_types ) + { + const auto& t = element->GetType(); + yield_types->insert(types_it, element->GetType()); + vector_val->insert(it, ZVal(std::move(element), t)); + } + else + vector_val->insert(it, ZVal(std::move(element), yield_type)); Modified(); return true; @@ -3248,6 +3314,12 @@ bool VectorVal::Remove(unsigned int index) auto it = std::next(vector_val->begin(), index); vector_val->erase(it); + if ( yield_types ) + { + auto types_it = std::next(yield_types->begin(), index); + yield_types->erase(types_it); + } + Modified(); return true; } @@ -3276,17 +3348,19 @@ bool VectorVal::AddTo(Val* val, bool /* is_first_init */) const return true; } -const ValPtr& VectorVal::At(unsigned int index) const +ValPtr VectorVal::At(unsigned int index) const { if ( index >= vector_val->size() ) return Val::nil; - return (*vector_val)[index]; + const auto& t = yield_types ? (*yield_types)[index] : yield_type; + + return (*vector_val)[index].ToVal(t); } void VectorVal::Sort(bool cmp_func(const ValPtr& a, const ValPtr& b)) { - sort(vector_val->begin(), vector_val->end(), cmp_func); + // Placeholder - will be filled in by a later commit. } unsigned int VectorVal::Resize(unsigned int new_num_elements) @@ -3294,6 +3368,13 @@ unsigned int VectorVal::Resize(unsigned int new_num_elements) unsigned int oldsize = vector_val->size(); vector_val->reserve(new_num_elements); vector_val->resize(new_num_elements); + + if ( yield_types ) + { + yield_types->reserve(new_num_elements); + yield_types->resize(new_num_elements); + } + return oldsize; } @@ -3309,6 +3390,9 @@ unsigned int VectorVal::ResizeAtLeast(unsigned int new_num_elements) void VectorVal::Reserve(unsigned int num_elements) { vector_val->reserve(num_elements); + + if ( yield_types ) + yield_types->reserve(num_elements); } ValPtr VectorVal::DoClone(CloneState* state) @@ -3317,9 +3401,11 @@ ValPtr VectorVal::DoClone(CloneState* state) vv->Reserve(vector_val->size()); state->NewClone(this, vv); - for ( const auto& e : *vector_val ) + auto n = vector_val->size(); + + for ( auto i = 0; i < n; ++i ) { - auto vc = e->Clone(state); + auto vc = At(i)->Clone(state); vv->Append(std::move(vc)); } @@ -3332,18 +3418,21 @@ void VectorVal::ValDescribe(ODesc* d) const size_t vector_size = vector_val->size(); - if ( vector_size != 0) + if ( vector_size != 0 ) { - for ( unsigned int i = 0; i < (vector_size - 1); ++i ) + auto last_ind = vector_size - 1; + for ( unsigned int i = 0; i < last_ind; ++i ) { - if ( vector_val->at(i) ) - vector_val->at(i)->Describe(d); + auto v = At(i); + if ( v ) + v->Describe(d); d->Add(", "); } - } - if ( vector_size != 0 && vector_val->back() ) - vector_val->back()->Describe(d); + auto v = At(last_ind); + if ( v ) + v->Describe(d); + } d->Add("]"); } diff --git a/src/Val.h b/src/Val.h index b242b5ba3a..5380fec458 100644 --- a/src/Val.h +++ b/src/Val.h @@ -1305,7 +1305,7 @@ public: * @return The element at the given index or nullptr if the index * does not exist (it's greater than or equal to vector's current size). */ - const ValPtr& At(unsigned int index) const; + ValPtr At(unsigned int index) const; /** * Returns the given element treated as a Count type, to efficiently @@ -1364,7 +1364,29 @@ protected: ValPtr DoClone(CloneState* state) override; private: - std::vector* vector_val; + // Check the type of the given element against our current + // yield type and adjust as necessary. Returns whether the + // element type-checked. + bool CheckElementType(const ValPtr& element); + + std::vector* vector_val; + + // For homogeneous vectors (the usual case), the type of the + // elements. Will be TYPE_VOID for empty vectors created using + // "vector()". + TypePtr yield_type; + + // True if this is a vector-of-any, or potentially one (which is + // the case for empty vectors created using "vector()"). + bool any_yield; + + // For heterogeneous vectors, the individual type of each element, + // parallel to vector_val. Heterogeneous vectors can arise for + // "vector of any" when disparate elements are stored in the vector. + // + // Thus, if yield_types is non-nil, then we know this is a + // vector-of-any. + std::vector* yield_types = nullptr; }; #define UNDERLYING_ACCESSOR_DEF(ztype, ctype, name) \ diff --git a/src/zeek.bif b/src/zeek.bif index eb936c8a08..845c8c7a14 100644 --- a/src/zeek.bif +++ b/src/zeek.bif @@ -1513,7 +1513,8 @@ function order%(v: any, ...%) : index_vec for ( i = 0; i < n; ++i ) { ind_vv[i] = i; - index_map.emplace_back(&vv->At(i)); + auto tmp_until_later_commit = vv->At(i); + index_map.emplace_back(&tmp_until_later_commit); } if ( comp )