struct CryptomatteManifestReader { static constexpr int skip_whitespaces_len_(blender::StringRef ref) { int skip_len = 0; while (skip_len < ref.size()) { char front = ref[skip_len]; if (!std::isspace(front, std::locale::classic())) { break; } skip_len++; } return skip_len; } static constexpr blender::StringRef skip_whitespaces_(blender::StringRef ref) { return ref.drop_prefix(skip_whitespaces_len_(ref)); } static constexpr int quoted_string_len_(blender::StringRef ref) { int len = 1; bool skip_next = false; while (len < ref.size()) { char current_char = ref[len]; if (skip_next) { skip_next = false; } else { if (current_char == '\\') { skip_next = true; } if (current_char == '\"') { len += 1; break; } } len += 1; } return len; } static std::string unquote_(const blender::StringRef ref) { std::ostringstream stream; for (char c : ref) { if (c != '\\') { stream << c; } } return stream.str(); } static uint32_t decode_hash_(const blender::StringRef ref) { uint32_t result; std::istringstream(ref) >> std::hex >> result; return result; } static bool parse(CryptomatteLayer &layer, blender::StringRefNull manifest) { StringRef ref = manifest; ref = skip_whitespaces_(ref); if (ref.is_empty() || ref.front() != '{') { return false; } ref = ref.drop_prefix(1); while (!ref.is_empty()) { char front = ref.front(); if (front == '\"') { const int quoted_name_len = quoted_string_len_(ref); const int name_len = quoted_name_len - 2; std::string name = unquote_(ref.substr(1, name_len)); ref = ref.drop_prefix(quoted_name_len); ref = skip_whitespaces_(ref); char colon = ref.front(); if (colon != ':') { return false; } ref = ref.drop_prefix(1); ref = skip_whitespaces_(ref); if (ref.front() != '\"') { return false; } const int quoted_hash_len = quoted_string_len_(ref); const int hash_len = quoted_hash_len - 2; uint32_t hash = decode_hash_(ref.substr(1, hash_len)); ref = ref.drop_prefix(quoted_hash_len); layer.add_hash(name, hash); } else if (front == ',') { ref = ref.drop_prefix(1); } else if (front == '}') { ref = ref.drop_prefix(1); ref = skip_whitespaces_(ref); break; } ref = skip_whitespaces_(ref); } if (!ref.is_empty()) { return false; } return true; } };