/* * Copyright (c) Schrodinger, LLC. * * Datastructure for `bond_dict`, parsed from CIF files with * `chem_comp_bond` records. * * This file includes a generic reference implementation (1), and an * optimized fast and memory efficient implementation (2) with key * string length limitations. */ #ifndef CIFBONDDICT_H #define CIFBONDDICT_H #include #include #include #include "os_std.h" #include "PyMOLGlobals.h" #if 1 // (2) // Optimized implementation, tuned on speed and memory usage. // Only supports resn length <= 8 and atom name length <= 4. // Working with this limitation is currently reasonable in PyMOL, // since cResnLen=5 and cAtomNameLen=4 (see AtomInfo.h). #include #include #include // mapped type of bond_dict_t class res_bond_dict_t { using key_type = std::int_fast64_t; using halfkey_t = std::int32_t; using mapped_type = signed char; static halfkey_t make_halfkey(const char* name) { union { halfkey_t i; char s[sizeof(halfkey_t)]; }; strncpy(s, name, sizeof(halfkey_t)); return i; } static key_type make_key(const char * name1, const char * name2) { auto i1 = make_halfkey(name1); auto i2 = make_halfkey(name2); if (i1 > i2) std::swap(i1, i2); // construct 8-byte key from the two 4-byte names return (((key_type)i1) << 32) | i2; } std::unordered_map m_map; std::unordered_map m_alt_names; const char* get_standard_name(const char* name) const { auto it = m_alt_names.find(make_halfkey(name)); if (it != m_alt_names.end()) { return it->second.c_str(); } return name; } public: void add_alt_name(const char* name, const char* alt) { m_alt_names[make_halfkey(alt)] = name; } void set(const char * name1, const char * name2, mapped_type order) { m_map[make_key(name1, name2)] = order; } mapped_type get(const char * name1, const char * name2) const { name1 = get_standard_name(name1); name2 = get_standard_name(name2); auto it = m_map.find(make_key(name1, name2)); if (it == m_map.end()) return -1; return it->second; } }; // type for mapping: resn -> ((name1, name2) -> bond order) class bond_dict_t { using key_type = std::int_fast64_t; using mapped_type = res_bond_dict_t; static key_type make_key(const char * s_) { union { key_type i; char s[sizeof(key_type)]; }; strncpy(s, s_, sizeof(s)); return i; } std::map m_map; std::set unknown_resn; public: bool empty() const { return m_map.empty(); } mapped_type& operator[](const char* resn) { return m_map[make_key(resn)]; } void set_unknown(const char * resn) { unknown_resn.insert(make_key(resn)); } const mapped_type * get(PyMOLGlobals *, const char * resn, bool try_download=true); }; #else // (1) // Generic reference implementation. Supports arbitrary key lengths, // but is slower and less memory efficient. Equivalent to: // // typedef std::map > > bond_dict_t; // // with added get/set methods. #include class res_bond_dict_t { std::map > m_data; public: void set(const char * name1, const char * name2, int order) { if (strcmp(name1, name2) < 0) std::swap(name1, name2); m_data[name1][name2] = order; } int get(const char * name1, const char * name2) const { if (strcmp(name1, name2) < 0) std::swap(name1, name2); try { return m_data.at(name1).at(name2); } catch (const std::out_of_range& e) { return -1; } } }; class bond_dict_t { std::map m_data; public: void set(const char * resn, const char * name1, const char * name2, int order) { m_data[resn].set(name1, name2, order); } const res_bond_dict_t * get(const char * resn) const { auto it = m_data.find(resn); if (it == m_data.end()) return nullptr; return &it->second; } bool empty() const { return m_data.empty(); } }; #endif #endif