// file : build2/variable.txx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include namespace build2 { template bool lookup:: belongs (const T& x, bool t) const { if (vars == &x.vars) return true; if (t) { for (const auto& p1: x.target_vars) // variable_type_map { for (const auto& p2: p1.second) // variable_pattern_map { if (vars == &p2.second) return true; } } } return false; } // This one will be SFINAE'd out unless T is a simple value. // template auto convert (names&& ns) -> decltype (value_traits::convert (move (ns[0]), nullptr)) { size_t n (ns.size ()); if (n == 0) { if (value_traits::empty_value) return T (); } else if (n == 1) { return convert (move (ns[0])); } else if (n == 2 && ns[0].pair != '\0') { return convert (move (ns[0]), move (ns[1])); } throw invalid_argument ( string ("invalid ") + value_traits::type_name + (n == 0 ? " value: empty" : " value: multiple names")); } template T convert (value&& v) { if (v.type == nullptr) return convert (move (v).as ()); else if (v.type == &value_traits::value_type) return move (v).as (); throw invalid_argument ( string ("invalid ") + value_traits::value_type.name + " value: conversion from " + v.type->name); } template void default_dtor (value& v) { v.as ().~T (); } template void default_copy_ctor (value& l, const value& r, bool m) { if (m) new (&l.data_) T (move (const_cast (r).as ())); else new (&l.data_) T (r.as ()); } template void default_copy_assign (value& l, const value& r, bool m) { if (m) l.as () = move (const_cast (r).as ()); else l.as () = r.as (); } template bool default_empty (const value& v) { return value_traits::empty (v.as ()); } template void simple_assign (value& v, names&& ns, const variable* var) { size_t n (ns.size ()); if (value_traits::empty_value ? n <= 1 : n == 1) { try { value_traits::assign ( v, (n == 0 ? T () : value_traits::convert (move (ns.front ()), nullptr))); return; } catch (const invalid_argument&) {} // Fall through. } diag_record dr (fail); dr << "invalid " << value_traits::value_type.name << " value '" << ns << "'"; if (var != nullptr) dr << " in variable " << var->name; } template void simple_append (value& v, names&& ns, const variable* var) { size_t n (ns.size ()); if (value_traits::empty_value ? n <= 1 : n == 1) { try { value_traits::append ( v, (n == 0 ? T () : value_traits::convert (move (ns.front ()), nullptr))); return; } catch (const invalid_argument&) {} // Fall through. } diag_record dr (fail); dr << "invalid " << value_traits::value_type.name << " value '" << ns << "'"; if (var != nullptr) dr << " in variable " << var->name; } template void simple_prepend (value& v, names&& ns, const variable* var) { size_t n (ns.size ()); if (value_traits::empty_value ? n <= 1 : n == 1) { try { value_traits::prepend ( v, (n == 0 ? T () : value_traits::convert (move (ns.front ()), nullptr))); return; } catch (const invalid_argument&) {} // Fall through. } diag_record dr (fail); dr << "invalid " << value_traits::value_type.name << " value '" << ns << "'"; if (var != nullptr) dr << " in variable " << var->name; } template names_view simple_reverse (const value& v, names& s) { const T& x (v.as ()); // Represent an empty simple value as empty name sequence rather than // a single empty name. This way, for example, during serialization we // end up with a much saner looking: // // config.import.foo = // // Rather than: // // config.import.foo = {} // if (!value_traits::empty (x)) s.emplace_back (value_traits::reverse (x)); return s; } template int simple_compare (const value& l, const value& r) { return value_traits::compare (l.as (), r.as ()); } // vector value // template vector value_traits>:: convert (names&& ns) { vector v; // Similar to vector_append() below except we throw instead of issuing // diagnostics. // for (auto i (ns.begin ()); i != ns.end (); ++i) { name& n (*i); name* r (nullptr); if (n.pair) { r = &*++i; if (n.pair != '@') throw invalid_argument ( string ("invalid pair character: '") + n.pair + "'"); } v.push_back (value_traits::convert (move (n), r)); } return v; } template void vector_append (value& v, names&& ns, const variable* var) { vector& p (v ? v.as> () : *new (&v.data_) vector ()); // Convert each element to T while merging pairs. // for (auto i (ns.begin ()); i != ns.end (); ++i) { name& n (*i); name* r (nullptr); if (n.pair) { r = &*++i; if (n.pair != '@') { diag_record dr (fail); dr << "unexpected pair style for " << value_traits::value_type.name << " value " << "'" << n << "'" << n.pair << "'" << *r << "'"; if (var != nullptr) dr << " in variable " << var->name; } } try { p.push_back (value_traits::convert (move (n), r)); } catch (const invalid_argument&) { diag_record dr (fail); dr << "invalid " << value_traits::value_type.name; if (n.pair) dr << " element pair '" << n << "'@'" << *r << "'"; else dr << " element '" << n << "'"; if (var != nullptr) dr << " in variable " << var->name; } } } template void vector_assign (value& v, names&& ns, const variable* var) { if (v) v.as> ().clear (); vector_append (v, move (ns), var); } template void vector_prepend (value& v, names&& ns, const variable* var) { // Reduce to append. // vector t; vector* p; if (v) { p = &v.as> (); p->swap (t); } else p = new (&v.data_) vector (); vector_append (v, move (ns), var); p->insert (p->end (), make_move_iterator (t.begin ()), make_move_iterator (t.end ())); } template static names_view vector_reverse (const value& v, names& s) { auto& vv (v.as> ()); s.reserve (vv.size ()); for (const T& x: vv) s.push_back (value_traits::reverse (x)); return s; } template static int vector_compare (const value& l, const value& r) { auto& lv (l.as> ()); auto& rv (r.as> ()); auto li (lv.begin ()), le (lv.end ()); auto ri (rv.begin ()), re (rv.end ()); for (; li != le && ri != re; ++li, ++ri) if (int r = value_traits::compare (*li, *ri)) return r; if (li == le && ri != re) // l shorter than r. return -1; if (ri == re && li != le) // r shorter than l. return 1; return 0; } template value_traits>::value_type_ex:: value_type_ex (value_type&& v) : value_type (move (v)) { type_name = value_traits::type_name; type_name += 's'; name = type_name.c_str (); } template const vector value_traits>::empty_instance; template const typename value_traits>::value_type_ex value_traits>::value_type = build2::value_type // VC14 wants =. { nullptr, // Patched above. sizeof (vector), nullptr, // No base. &value_traits::value_type, &default_dtor>, &default_copy_ctor>, &default_copy_assign>, &vector_assign, &vector_append, &vector_prepend, &vector_reverse, nullptr, // No cast (cast data_ directly). &vector_compare, &default_empty> }; // map value // template void map_append (value& v, names&& ns, const variable* var) { using std::map; map& p (v ? v.as> () : *new (&v.data_) map ()); // Verify we have a sequence of pairs and convert each lhs/rhs to K/V. // for (auto i (ns.begin ()); i != ns.end (); ++i) { name& l (*i); if (!l.pair) { diag_record dr (fail); dr << value_traits>::value_type.name << " key-value " << "pair expected instead of '" << l << "'"; if (var != nullptr) dr << " in variable " << var->name; } name& r (*++i); // Got to have the second half of the pair. if (l.pair != '@') { diag_record dr (fail); dr << "unexpected pair style for " << value_traits>::value_type.name << " key-value " << "'" << l << "'" << l.pair << "'" << r << "'"; if (var != nullptr) dr << " in variable " << var->name; } try { K k (value_traits::convert (move (l), nullptr)); try { V v (value_traits::convert (move (r), nullptr)); p.emplace (move (k), move (v)); } catch (const invalid_argument&) { diag_record dr (fail); dr << "invalid " << value_traits::value_type.name << " element value '" << r << "'"; if (var != nullptr) dr << " in variable " << var->name; } } catch (const invalid_argument&) { diag_record dr (fail); dr << "invalid " << value_traits::value_type.name << " element key '" << l << "'"; if (var != nullptr) dr << " in variable " << var->name; } } } template void map_assign (value& v, names&& ns, const variable* var) { using std::map; if (v) v.as> ().clear (); map_append (v, move (ns), var); } template static names_view map_reverse (const value& v, names& s) { using std::map; auto& vm (v.as> ()); s.reserve (2 * vm.size ()); for (const auto& p: vm) { s.push_back (value_traits::reverse (p.first)); s.back ().pair = '@'; s.push_back (value_traits::reverse (p.second)); } return s; } template static int map_compare (const value& l, const value& r) { using std::map; auto& lm (l.as> ()); auto& rm (r.as> ()); auto li (lm.begin ()), le (lm.end ()); auto ri (rm.begin ()), re (rm.end ()); for (; li != le && ri != re; ++li, ++ri) { int r; if ((r = value_traits::compare (li->first, ri->first)) != 0 || (r = value_traits::compare (li->second, ri->second)) != 0) return r; } if (li == le && ri != re) // l shorter than r. return -1; if (ri == re && li != le) // r shorter than l. return 1; return 0; } template value_traits>::value_type_ex:: value_type_ex (value_type&& v) : value_type (move (v)) { type_name = value_traits::type_name; type_name += '_'; type_name += value_traits::type_name; type_name += "_map"; name = type_name.c_str (); } template const std::map value_traits>::empty_instance; template const typename value_traits>::value_type_ex value_traits>::value_type = build2::value_type // VC14 wants = { nullptr, // Patched above. sizeof (map), nullptr, // No base. nullptr, // No element. &default_dtor>, &default_copy_ctor>, &default_copy_assign>, &map_assign, &map_append, &map_append, // Prepend is the same as append. &map_reverse, nullptr, // No cast (cast data_ directly). &map_compare, &default_empty> }; // variable_cache // template pair variable_cache:: insert (K k, const lookup& stem, size_t ver, const variable& var) { using value_data = variable_map::value_data; const variable_map* svars (stem.vars); // NULL if undefined. size_t sver (stem.defined () ? static_cast (stem.value)->version : 0); shared_mutex& m ( variable_cache_mutex_shard[ hash () (this) % variable_cache_mutex_shard_size]); slock sl (m); ulock ul (m, defer_lock); auto i (m_.find (k)); // Cache hit. // if (i != m_.end () && i->second.version == ver && i->second.stem_vars == svars && i->second.stem_version == sver && (var.type == nullptr || i->second.value.type == var.type)) return pair (i->second.value, move (ul)); // Relock for exclusive access. Note that it is entirely possible // that between unlock and lock someone else has updated the entry. // sl.unlock (); ul.lock (); // Note that the cache entries are never removed so we can reuse the // iterator. // pair p (i, i == m_.end ()); if (p.second) p = m_.emplace (move (k), entry_type {value_data (nullptr), ver, svars, sver}); entry_type& e (p.first->second); if (p.second) { // Cache miss. // e.value.version++; // New value. } else if (e.version != ver || e.stem_vars != svars || e.stem_version != sver) { // Cache invalidation. // assert (e.version <= ver); e.version = ver; if (e.stem_vars != svars) e.stem_vars = svars; else assert (e.stem_version <= sver); e.stem_version = sver; e.value.version++; // Value changed. } else { // Cache hit. // if (var.type != nullptr && e.value.type != var.type) typify (e.value, *var.type, &var); ul.unlock (); } return pair (e.value, move (ul)); } }