<commit_before>// Copyright 2015-2018 Elviss Strazdins. All rights reserved.

#include "core/Setup.h"

#if OUZEL_PLATFORM_ANDROID && OUZEL_COMPILE_OPENGL

#include "RenderDeviceOGLAndroid.hpp"
#include "core/android/NativeWindowAndroid.hpp"
#include "core/Engine.hpp"
#include "utils/Errors.hpp"
#include "utils/Log.hpp"
#include "utils/Utils.hpp"

namespace ouzel
{
    namespace graphics
    {
        RenderDeviceOGLAndroid::~RenderDeviceOGLAndroid()
        {
            running = false;
            flushCommands();
            if (renderThread.joinable()) renderThread.join();

            if (context)
            {
                eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
                eglDestroyContext(display, context);
            }

            if (surface)
                eglDestroySurface(display, surface);

            if (display)
                eglTerminate(display);
        }

        void RenderDeviceOGLAndroid::init(Window* newWindow,
                                          const Size2&,
                                          uint32_t newSampleCount,
                                          Texture::Filter newTextureFilter,
                                          uint32_t newMaxAnisotropy,
                                          bool newVerticalSync,
                                          bool newDepth,
                                          bool newDebugRenderer)
        {
            display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

            if (!display)
                throw SystemError("Failed to get display");

            if (!eglInitialize(display, nullptr, nullptr))
                throw SystemError("Failed to initialize EGL");

            const EGLint attributeList[] =
            {
                EGL_RED_SIZE, 8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE, 8,
                EGL_ALPHA_SIZE, 8,
                EGL_DEPTH_SIZE, newDepth ? 24 : 0,
                EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
                EGL_SAMPLE_BUFFERS, (newSampleCount > 1) ? 1 : 0,
                EGL_SAMPLES, static_cast<int>(newSampleCount),
                EGL_NONE
            };
            EGLConfig config;
            EGLint numConfig;
            if (!eglChooseConfig(display, attributeList, &config, 1, &numConfig))
                throw SystemError("Failed to choose EGL config");

            if (!eglBindAPI(EGL_OPENGL_ES_API))
                throw SystemError("Failed to bind OpenGL ES API");

            EGLint format;
            if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format))
                throw SystemError("Failed to get config attribute " + std::to_string(eglGetError()));

            NativeWindowAndroid* windowAndroid = static_cast<NativeWindowAndroid*>(newWindow->getNativeWindow());

            ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

            surface = eglCreateWindowSurface(display, config, windowAndroid->getNativeWindow(), nullptr);
            if (surface == EGL_NO_SURFACE)
                throw SystemError("Failed to create EGL window surface");

            for (EGLint version = 3; version >= 2; --version)
            {
                std::vector<EGLint> contextAttributes =
                {
                    EGL_CONTEXT_CLIENT_VERSION, version
                };

                if (newDebugRenderer)
                {
                    contextAttributes.push_back(EGL_CONTEXT_FLAGS_KHR);
                    contextAttributes.push_back(EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR);
                }

                contextAttributes.push_back(EGL_NONE);

                context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes.data());

                if (context != EGL_NO_CONTEXT)
                {
                    apiMajorVersion = version;
                    apiMinorVersion = 0;
                    Log(Log::Level::INFO) << "EGL OpenGL ES " << version << " context created";
                    break;
                }
            }

            if (context == EGL_NO_CONTEXT)
                throw SystemError("Failed to create EGL context");

            if (!eglMakeCurrent(display, surface, surface, context))
                throw SystemError("Failed to set current EGL context");

            if (!eglSwapInterval(display, newVerticalSync ? 1 : 0))
                throw SystemError("Failed to set EGL frame interval");

            EGLint surfaceWidth;
            EGLint surfaceHeight;

            if (!eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth) ||
                !eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight))
                throw SystemError("Failed to get query window size, error: " + std::to_string(eglGetError()));

            frameBufferWidth = surfaceWidth;
            frameBufferHeight = surfaceHeight;

            Size2 backBufferSize = Size2(static_cast<float>(frameBufferWidth),
                                         static_cast<float>(frameBufferHeight));

            RenderDeviceOGL::init(newWindow,
                                  backBufferSize,
                                  newSampleCount,
                                  newTextureFilter,
                                  newMaxAnisotropy,
                                  newVerticalSync,
                                  newDepth,
                                  newDebugRenderer);

            if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                throw SystemError("Failed to unset EGL context");

            running = true;
            renderThread = std::thread(&RenderDeviceOGLAndroid::main, this);
        }

        void RenderDeviceOGLAndroid::reload()
        {
            running = false;
            flushCommands();
            if (renderThread.joinable()) renderThread.join();

            const EGLint attributeList[] =
            {
                EGL_RED_SIZE, 8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE, 8,
                EGL_ALPHA_SIZE, 8,
                EGL_DEPTH_SIZE, depth ? 24 : 0,
                EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
                EGL_SAMPLE_BUFFERS, (sampleCount > 1) ? 1 : 0,
                EGL_SAMPLES, static_cast<int>(sampleCount),
                EGL_NONE
            };
            EGLConfig config;
            EGLint numConfig;
            if (!eglChooseConfig(display, attributeList, &config, 1, &numConfig))
                throw SystemError("Failed to choose EGL config");

            if (!eglBindAPI(EGL_OPENGL_ES_API))
                throw SystemError("Failed to bind OpenGL ES API");

            EGLint format;
            if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format))
                throw SystemError("Failed to get config attribute " + std::to_string(eglGetError()));

            NativeWindowAndroid* windowAndroid = static_cast<NativeWindowAndroid*>(window->getNativeWindow());

            ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

            surface = eglCreateWindowSurface(display, config, windowAndroid->getNativeWindow(), nullptr);
            if (surface == EGL_NO_SURFACE)
                throw SystemError("Failed to create EGL window surface");

            for (EGLint version = 3; version >= 2; --version)
            {
                std::vector<EGLint> contextAttributes =
                {
                    EGL_CONTEXT_CLIENT_VERSION, version
                };

                if (debugRenderer)
                {
                    contextAttributes.push_back(EGL_CONTEXT_FLAGS_KHR);
                    contextAttributes.push_back(EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR);
                }

                contextAttributes.push_back(EGL_NONE);

                context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes.data());

                if (context != EGL_NO_CONTEXT)
                {
                    apiMajorVersion = version;
                    apiMinorVersion = 0;
                    Log(Log::Level::INFO) << "EGL OpenGL ES " << version << " context created";
                    break;
                }
            }

            if (context == EGL_NO_CONTEXT)
                throw SystemError("Failed to create EGL context");

            if (!eglMakeCurrent(display, surface, surface, context))
                throw SystemError("Failed to set current EGL context");

            if (!eglSwapInterval(display, verticalSync ? 1 : 0))
                throw SystemError("Failed to set EGL frame interval");

            EGLint surfaceWidth;
            EGLint surfaceHeight;

            if (!eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth) ||
                !eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight))
                throw SystemError("Failed to get query window size " + std::to_string(eglGetError()));

            frameBufferWidth = surfaceWidth;
            frameBufferHeight = surfaceHeight;

            stateCache = StateCache();

            glDisable(GL_DITHER);
            glDepthFunc(GL_LEQUAL);

            GLenum error;

            if ((error = glGetError()) != GL_NO_ERROR)
                throw SystemError("Failed to set depth function, error: " + std::to_string(error));

            if (glGenVertexArraysProc) glGenVertexArraysProc(1, &vertexArrayId);

            {
                std::unique_lock<std::mutex> lock(resourceMutex);

                for (const std::unique_ptr<RenderResource>& resource : resources)
                    resource->reload();
            }

            if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                throw SystemError("Failed to unset EGL context");

            running = true;
            renderThread = std::thread(&RenderDeviceOGLAndroid::main, this);
        }

        void RenderDeviceOGLAndroid::destroy()
        {
            running = false;
            flushCommands();
            if (renderThread.joinable()) renderThread.join();

            if (context)
            {
                if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                    Log(Log::Level::ERR) << "Failed to unset EGL context";

                if (!eglDestroyContext(display, context))
                    Log(Log::Level::ERR) << "Failed to destroy EGL context";

                context = nullptr;
            }

            if (surface)
            {
                if (!eglDestroySurface(display, surface))
                    Log(Log::Level::ERR) << "Failed to destroy EGL surface";

                surface = nullptr;
            }
        }

        void RenderDeviceOGLAndroid::present()
        {
            if (eglSwapBuffers(display, surface) != EGL_TRUE)
                throw SystemError("Failed to swap buffers, error: " + std::to_string(eglGetError()));
        }

        void RenderDeviceOGLAndroid::main()
        {
            setCurrentThreadName("Render");

            if (!eglMakeCurrent(display, surface, surface, context))
                throw SystemError("Failed to set current EGL context, error: " + std::to_string(eglGetError()));

            while (running)
            {
                try
                {
                    process();
                }
                catch (const std::exception& e)
                {
                    Log(Log::Level::ERR) << e.what();
                }
                catch (...)
                {
                    Log(Log::Level::ERR) << "Unknown error occurred";
                }
            }

            if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                Log(Log::Level::ERR) << "Failed to unset EGL context, error: " << eglGetError();
        }
    } // namespace graphics
} // namespace ouzel

#endif
<commit_msg>Cast the RenderResource to RenderResourceOGL before calling reload() on it<commit_after>// Copyright 2015-2018 Elviss Strazdins. All rights reserved.

#include "core/Setup.h"

#if OUZEL_PLATFORM_ANDROID && OUZEL_COMPILE_OPENGL

#include "RenderDeviceOGLAndroid.hpp"
#include "core/android/NativeWindowAndroid.hpp"
#include "core/Engine.hpp"
#include "utils/Errors.hpp"
#include "utils/Log.hpp"
#include "utils/Utils.hpp"

namespace ouzel
{
    namespace graphics
    {
        RenderDeviceOGLAndroid::~RenderDeviceOGLAndroid()
        {
            running = false;
            flushCommands();
            if (renderThread.joinable()) renderThread.join();

            if (context)
            {
                eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
                eglDestroyContext(display, context);
            }

            if (surface)
                eglDestroySurface(display, surface);

            if (display)
                eglTerminate(display);
        }

        void RenderDeviceOGLAndroid::init(Window* newWindow,
                                          const Size2&,
                                          uint32_t newSampleCount,
                                          Texture::Filter newTextureFilter,
                                          uint32_t newMaxAnisotropy,
                                          bool newVerticalSync,
                                          bool newDepth,
                                          bool newDebugRenderer)
        {
            display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

            if (!display)
                throw SystemError("Failed to get display");

            if (!eglInitialize(display, nullptr, nullptr))
                throw SystemError("Failed to initialize EGL");

            const EGLint attributeList[] =
            {
                EGL_RED_SIZE, 8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE, 8,
                EGL_ALPHA_SIZE, 8,
                EGL_DEPTH_SIZE, newDepth ? 24 : 0,
                EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
                EGL_SAMPLE_BUFFERS, (newSampleCount > 1) ? 1 : 0,
                EGL_SAMPLES, static_cast<int>(newSampleCount),
                EGL_NONE
            };
            EGLConfig config;
            EGLint numConfig;
            if (!eglChooseConfig(display, attributeList, &config, 1, &numConfig))
                throw SystemError("Failed to choose EGL config");

            if (!eglBindAPI(EGL_OPENGL_ES_API))
                throw SystemError("Failed to bind OpenGL ES API");

            EGLint format;
            if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format))
                throw SystemError("Failed to get config attribute " + std::to_string(eglGetError()));

            NativeWindowAndroid* windowAndroid = static_cast<NativeWindowAndroid*>(newWindow->getNativeWindow());

            ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

            surface = eglCreateWindowSurface(display, config, windowAndroid->getNativeWindow(), nullptr);
            if (surface == EGL_NO_SURFACE)
                throw SystemError("Failed to create EGL window surface");

            for (EGLint version = 3; version >= 2; --version)
            {
                std::vector<EGLint> contextAttributes =
                {
                    EGL_CONTEXT_CLIENT_VERSION, version
                };

                if (newDebugRenderer)
                {
                    contextAttributes.push_back(EGL_CONTEXT_FLAGS_KHR);
                    contextAttributes.push_back(EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR);
                }

                contextAttributes.push_back(EGL_NONE);

                context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes.data());

                if (context != EGL_NO_CONTEXT)
                {
                    apiMajorVersion = version;
                    apiMinorVersion = 0;
                    Log(Log::Level::INFO) << "EGL OpenGL ES " << version << " context created";
                    break;
                }
            }

            if (context == EGL_NO_CONTEXT)
                throw SystemError("Failed to create EGL context");

            if (!eglMakeCurrent(display, surface, surface, context))
                throw SystemError("Failed to set current EGL context");

            if (!eglSwapInterval(display, newVerticalSync ? 1 : 0))
                throw SystemError("Failed to set EGL frame interval");

            EGLint surfaceWidth;
            EGLint surfaceHeight;

            if (!eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth) ||
                !eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight))
                throw SystemError("Failed to get query window size, error: " + std::to_string(eglGetError()));

            frameBufferWidth = surfaceWidth;
            frameBufferHeight = surfaceHeight;

            Size2 backBufferSize = Size2(static_cast<float>(frameBufferWidth),
                                         static_cast<float>(frameBufferHeight));

            RenderDeviceOGL::init(newWindow,
                                  backBufferSize,
                                  newSampleCount,
                                  newTextureFilter,
                                  newMaxAnisotropy,
                                  newVerticalSync,
                                  newDepth,
                                  newDebugRenderer);

            if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                throw SystemError("Failed to unset EGL context");

            running = true;
            renderThread = std::thread(&RenderDeviceOGLAndroid::main, this);
        }

        void RenderDeviceOGLAndroid::reload()
        {
            running = false;
            flushCommands();
            if (renderThread.joinable()) renderThread.join();

            const EGLint attributeList[] =
            {
                EGL_RED_SIZE, 8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE, 8,
                EGL_ALPHA_SIZE, 8,
                EGL_DEPTH_SIZE, depth ? 24 : 0,
                EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
                EGL_SAMPLE_BUFFERS, (sampleCount > 1) ? 1 : 0,
                EGL_SAMPLES, static_cast<int>(sampleCount),
                EGL_NONE
            };
            EGLConfig config;
            EGLint numConfig;
            if (!eglChooseConfig(display, attributeList, &config, 1, &numConfig))
                throw SystemError("Failed to choose EGL config");

            if (!eglBindAPI(EGL_OPENGL_ES_API))
                throw SystemError("Failed to bind OpenGL ES API");

            EGLint format;
            if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format))
                throw SystemError("Failed to get config attribute " + std::to_string(eglGetError()));

            NativeWindowAndroid* windowAndroid = static_cast<NativeWindowAndroid*>(window->getNativeWindow());

            ANativeWindow_setBuffersGeometry(windowAndroid->getNativeWindow(), 0, 0, format);

            surface = eglCreateWindowSurface(display, config, windowAndroid->getNativeWindow(), nullptr);
            if (surface == EGL_NO_SURFACE)
                throw SystemError("Failed to create EGL window surface");

            for (EGLint version = 3; version >= 2; --version)
            {
                std::vector<EGLint> contextAttributes =
                {
                    EGL_CONTEXT_CLIENT_VERSION, version
                };

                if (debugRenderer)
                {
                    contextAttributes.push_back(EGL_CONTEXT_FLAGS_KHR);
                    contextAttributes.push_back(EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR);
                }

                contextAttributes.push_back(EGL_NONE);

                context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes.data());

                if (context != EGL_NO_CONTEXT)
                {
                    apiMajorVersion = version;
                    apiMinorVersion = 0;
                    Log(Log::Level::INFO) << "EGL OpenGL ES " << version << " context created";
                    break;
                }
            }

            if (context == EGL_NO_CONTEXT)
                throw SystemError("Failed to create EGL context");

            if (!eglMakeCurrent(display, surface, surface, context))
                throw SystemError("Failed to set current EGL context");

            if (!eglSwapInterval(display, verticalSync ? 1 : 0))
                throw SystemError("Failed to set EGL frame interval");

            EGLint surfaceWidth;
            EGLint surfaceHeight;

            if (!eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth) ||
                !eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight))
                throw SystemError("Failed to get query window size " + std::to_string(eglGetError()));

            frameBufferWidth = surfaceWidth;
            frameBufferHeight = surfaceHeight;

            stateCache = StateCache();

            glDisable(GL_DITHER);
            glDepthFunc(GL_LEQUAL);

            GLenum error;

            if ((error = glGetError()) != GL_NO_ERROR)
                throw SystemError("Failed to set depth function, error: " + std::to_string(error));

            if (glGenVertexArraysProc) glGenVertexArraysProc(1, &vertexArrayId);

            {
                std::unique_lock<std::mutex> lock(resourceMutex);

                for (const std::unique_ptr<RenderResource>& resource : resources)
                    static_cast<RenderResourceOGL*>(resource)->reload();
            }

            if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                throw SystemError("Failed to unset EGL context");

            running = true;
            renderThread = std::thread(&RenderDeviceOGLAndroid::main, this);
        }

        void RenderDeviceOGLAndroid::destroy()
        {
            running = false;
            flushCommands();
            if (renderThread.joinable()) renderThread.join();

            if (context)
            {
                if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                    Log(Log::Level::ERR) << "Failed to unset EGL context";

                if (!eglDestroyContext(display, context))
                    Log(Log::Level::ERR) << "Failed to destroy EGL context";

                context = nullptr;
            }

            if (surface)
            {
                if (!eglDestroySurface(display, surface))
                    Log(Log::Level::ERR) << "Failed to destroy EGL surface";

                surface = nullptr;
            }
        }

        void RenderDeviceOGLAndroid::present()
        {
            if (eglSwapBuffers(display, surface) != EGL_TRUE)
                throw SystemError("Failed to swap buffers, error: " + std::to_string(eglGetError()));
        }

        void RenderDeviceOGLAndroid::main()
        {
            setCurrentThreadName("Render");

            if (!eglMakeCurrent(display, surface, surface, context))
                throw SystemError("Failed to set current EGL context, error: " + std::to_string(eglGetError()));

            while (running)
            {
                try
                {
                    process();
                }
                catch (const std::exception& e)
                {
                    Log(Log::Level::ERR) << e.what();
                }
                catch (...)
                {
                    Log(Log::Level::ERR) << "Unknown error occurred";
                }
            }

            if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
                Log(Log::Level::ERR) << "Failed to unset EGL context, error: " << eglGetError();
        }
    } // namespace graphics
} // namespace ouzel

#endif
<|endoftext|>


<commit_before>// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Utility/Formats/MD5MeshParser.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Utility/Config.hpp>
#include <cstdio>
#include <memory>
#include <Nazara/Utility/Debug.hpp>

namespace Nz
{
	MD5MeshParser::MD5MeshParser(Stream& stream) :
	m_stream(stream),
	m_streamFlags(stream.GetStreamOptions()), //< Saves stream flags
	m_keepLastLine(false),
	m_lineCount(0),
	m_meshIndex(0)
	{
		m_stream.EnableTextMode(true);
	}

	MD5MeshParser::~MD5MeshParser()
	{
		// Reset stream flags
		if ((m_streamFlags & StreamOption::Text) == 0)
			m_stream.EnableTextMode(false);
	}

	Ternary MD5MeshParser::Check()
	{
		if (Advance(false))
		{
			unsigned int version;
			if (std::sscanf(&m_currentLine[0], " MD5Version %u", &version) == 1)
			{
				if (version == 10)
					return Ternary::True;
			}
		}

		return Ternary::False;
	}

	const MD5MeshParser::Joint* MD5MeshParser::GetJoints() const
	{
		return m_joints.data();
	}

	UInt32 MD5MeshParser::GetJointCount() const
	{
		return static_cast<UInt32>(m_joints.size());
	}

	const MD5MeshParser::Mesh* MD5MeshParser::GetMeshes() const
	{
		return m_meshes.data();
	}

	UInt32 MD5MeshParser::GetMeshCount() const
	{
		return static_cast<UInt32>(m_meshes.size());
	}

	bool MD5MeshParser::Parse()
	{
		while (Advance(false))
		{
			switch (m_currentLine[0])
			{
				#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
				case 'M': // MD5Version
					if (!StartsWith(m_currentLine, "MD5Version "))
						UnrecognizedLine();
					break;

				case 'c': // commandline
					if (!StartsWith(m_currentLine, "commandline "))
						UnrecognizedLine();
					break;
				#endif

				case 'j': // joints
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					if (!StartsWith(m_currentLine, "joints {"))
					{
						UnrecognizedLine();
						break;
					}
					#endif

					if (!ParseJoints())
					{
						Error("Failed to parse joints");
						return false;
					}
					break;

				case 'm': // mesh
				{
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					if (!StartsWith(m_currentLine, "mesh {"))
					{
						UnrecognizedLine();
						break;
					}
					#endif

					if (m_meshIndex >= m_meshes.size())
					{
						#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						Warning("More meshes than registred");
						#endif

						m_meshes.emplace_back();
					}

					if (!ParseMesh())
					{
						NazaraError("Failed to parse mesh");
						return false;
					}

					m_meshIndex++;
					break;
				}

				case 'n': // num[Frames/Joints]
				{
					unsigned int count;
					if (std::sscanf(&m_currentLine[0], "numJoints %u", &count) == 1)
					{
						#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						if (!m_joints.empty())
							Warning("Joint count is already defined");
						#endif

						m_joints.resize(count);
					}
					else if (std::sscanf(&m_currentLine[0], "numMeshes %u", &count) == 1)
					{
						#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						if (!m_meshes.empty())
							Warning("Mesh count is already defined");
						#endif

						m_meshes.resize(count);
					}
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					else
						UnrecognizedLine();
					#endif
					break;
				}

				default:
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					UnrecognizedLine();
					#endif
					break;
			}
		}

		return true;
	}

	bool MD5MeshParser::Advance(bool required)
	{
		if (!m_keepLastLine)
		{
			do
			{
				if (m_stream.EndOfStream())
				{
					if (required)
						Error("Incomplete MD5 file");

					return false;
				}

				m_lineCount++;

				m_currentLine = m_stream.ReadLine();

				if (std::size_t p = m_currentLine.find("//"); p != m_currentLine.npos)
				{
					if (p > 0)
						m_currentLine = m_currentLine.substr(0, p - 1);
					else
						m_currentLine.clear();
				}

				// Trim left
				m_currentLine.erase(m_currentLine.begin(), std::find_if(m_currentLine.begin(), m_currentLine.end(), [](char c)
				{
					return !std::isspace(c);
				}));

				if (m_currentLine.empty())
					continue;
			}
			while (m_currentLine.empty());
		}
		else
			m_keepLastLine = false;

		return true;
	}

	void MD5MeshParser::Error(const std::string& message)
	{
		NazaraError(message + " at line #" + std::to_string(m_lineCount));
	}

	bool MD5MeshParser::ParseJoints()
	{
		std::size_t jointCount = m_joints.size();
		if (jointCount == 0)
		{
			Error("Joint count is invalid or missing");
			return false;
		}

		for (std::size_t i = 0; i < jointCount; ++i)
		{
			if (!Advance())
				return false;

			std::size_t pos = m_currentLine.find(' ');
			if (pos == std::string::npos)
			{
				UnrecognizedLine(true);
				return false;
			}

			if (pos >= 64)
			{
				NazaraError("Joint name is too long (>= 64 characters)");
				return false;
			}

			char name[64];
			if (std::sscanf(&m_currentLine[0], "%63s %d ( %f %f %f ) ( %f %f %f )", &name[0], &m_joints[i].parent,
			                                                                        &m_joints[i].bindPos.x, &m_joints[i].bindPos.y, &m_joints[i].bindPos.z,
			                                                                        &m_joints[i].bindOrient.x, &m_joints[i].bindOrient.y, &m_joints[i].bindOrient.z) != 8)
			{
				UnrecognizedLine(true);
				return false;
			}

			m_joints[i].name = Trim(name, '"');

			Int32 parent = m_joints[i].parent;
			if (parent >= 0)
			{
				if (static_cast<std::size_t>(parent) >= jointCount)
				{
					Error("Joint's parent is out of bounds (" + std::to_string(parent) + " >= " + std::to_string(jointCount) + ')');
					return false;
				}
			}

			m_joints[i].bindOrient.ComputeW(); // On calcule la composante W
		}

		if (!Advance())
			return false;

		if (m_currentLine != "}")
		{
			#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
			Warning("Hierarchy braces closing not found");
			#endif

			// On tente de survivre à l'erreur
			m_keepLastLine = true;
		}

		return true;
	}

	bool MD5MeshParser::ParseMesh()
	{
		bool finished = false;
		while (!finished && Advance(false))
		{
			switch (m_currentLine[0])
			{
				case '}':
					finished = true;
					break;

				case 's': // shader
				{
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					if (!StartsWith(m_currentLine, "shader "))
					{
						UnrecognizedLine();
						break;
					}
					#endif

					std::string_view shader = m_currentLine;
					shader = shader.substr(7);
					if (shader.empty())
					{
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						UnrecognizedLine();
#endif
						break;
					}

					if (shader.front() == '"')
						shader.remove_prefix(1);

					if (shader.empty())
					{
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						UnrecognizedLine();
#endif
						break;
					}

					if (shader.back() == '"')
						shader.remove_prefix(1);

					m_meshes[m_meshIndex].shader = shader;
					break;
				}

				case 'n': // num[tris/verts]
				{
					unsigned int count;
					if (std::sscanf(&m_currentLine[0], "numtris %u", &count) == 1)
					{
						m_meshes[m_meshIndex].triangles.resize(count);
						for (unsigned int i = 0; i < count; ++i)
						{
							if (!Advance())
								return false;

							Triangle& triangle = m_meshes[m_meshIndex].triangles[i];
							unsigned int index;
							if (std::sscanf(&m_currentLine[0], "tri %u %u %u %u", &index, &triangle.x, &triangle.y, &triangle.z) != 4)
							{
								UnrecognizedLine(true);
								return false;
							}

							if (index != i)
							{
								Error("Unexpected triangle index (expected " + std::to_string(i) + ", got " + std::to_string(index) + ')');
								return false;
							}
						}
					}
					else if (std::sscanf(&m_currentLine[0], "numverts %u", &count) == 1)
					{
						m_meshes[m_meshIndex].vertices.resize(count);
						for (unsigned int i = 0; i < count; ++i)
						{
							if (!Advance())
								return false;

							Vertex& vertex = m_meshes[m_meshIndex].vertices[i];
							unsigned int index;
							if (std::sscanf(&m_currentLine[0], "vert %u ( %f %f ) %u %u", &index, &vertex.uv.x, &vertex.uv.y, &vertex.startWeight, &vertex.weightCount) != 5)
							{
								UnrecognizedLine(true);
								return false;
							}

							if (index != i)
							{
								Error("Unexpected vertex index (expected " + std::to_string(i) + ", got " + std::to_string(index) + ')');
								return false;
							}
						}
					}
					else if (std::sscanf(&m_currentLine[0], "numweights %u", &count) == 1)
					{
						m_meshes[m_meshIndex].weights.resize(count);
						for (unsigned int i = 0; i < count; ++i)
						{
							if (!Advance())
								return false;

							Weight& weight = m_meshes[m_meshIndex].weights[i];
							unsigned int index;
							if (std::sscanf(&m_currentLine[0], "weight %u %u %f ( %f %f %f )", &index, &weight.joint, &weight.bias,
							                                                                   &weight.pos.x, &weight.pos.y, &weight.pos.z) != 6)
							{
								UnrecognizedLine(true);
								return false;
							}

							if (index != i)
							{
								Error("Unexpected weight index (expected " + std::to_string(i) + ", got " + std::to_string(index) + ')');
								return false;
							}
						}
					}
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					else
						UnrecognizedLine();
					#endif
					break;
				}

				default:
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					UnrecognizedLine();
					#endif
					break;
			}
		}

		if (m_meshes[m_meshIndex].triangles.empty())
		{
			NazaraError("Mesh has no triangles");
			return false;
		}

		if (m_meshes[m_meshIndex].vertices.empty())
		{
			NazaraError("Mesh has no vertices");
			return false;
		}

		if (m_meshes[m_meshIndex].weights.empty())
		{
			NazaraError("Mesh has no weights");
			return false;
		}

		#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
		if (!finished)
			Warning("Mesh braces closing not found");
		#endif

		return true;
	}

	void MD5MeshParser::Warning(const std::string& message)
	{
		NazaraWarning(message + " at line #" + std::to_string(m_lineCount));
	}

	void MD5MeshParser::UnrecognizedLine(bool error)
	{
		std::string message = "Unrecognized \"" + m_currentLine + '"';

		if (error)
			Error(message);
		else
			Warning(message);
}
}
<commit_msg>Utility/MD5Mesh: Fix shader reading<commit_after>// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Utility/Formats/MD5MeshParser.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Utility/Config.hpp>
#include <cstdio>
#include <memory>
#include <Nazara/Utility/Debug.hpp>

namespace Nz
{
	MD5MeshParser::MD5MeshParser(Stream& stream) :
	m_stream(stream),
	m_streamFlags(stream.GetStreamOptions()), //< Saves stream flags
	m_keepLastLine(false),
	m_lineCount(0),
	m_meshIndex(0)
	{
		m_stream.EnableTextMode(true);
	}

	MD5MeshParser::~MD5MeshParser()
	{
		// Reset stream flags
		if ((m_streamFlags & StreamOption::Text) == 0)
			m_stream.EnableTextMode(false);
	}

	Ternary MD5MeshParser::Check()
	{
		if (Advance(false))
		{
			unsigned int version;
			if (std::sscanf(&m_currentLine[0], " MD5Version %u", &version) == 1)
			{
				if (version == 10)
					return Ternary::True;
			}
		}

		return Ternary::False;
	}

	const MD5MeshParser::Joint* MD5MeshParser::GetJoints() const
	{
		return m_joints.data();
	}

	UInt32 MD5MeshParser::GetJointCount() const
	{
		return static_cast<UInt32>(m_joints.size());
	}

	const MD5MeshParser::Mesh* MD5MeshParser::GetMeshes() const
	{
		return m_meshes.data();
	}

	UInt32 MD5MeshParser::GetMeshCount() const
	{
		return static_cast<UInt32>(m_meshes.size());
	}

	bool MD5MeshParser::Parse()
	{
		while (Advance(false))
		{
			switch (m_currentLine[0])
			{
				#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
				case 'M': // MD5Version
					if (!StartsWith(m_currentLine, "MD5Version "))
						UnrecognizedLine();
					break;

				case 'c': // commandline
					if (!StartsWith(m_currentLine, "commandline "))
						UnrecognizedLine();
					break;
				#endif

				case 'j': // joints
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					if (!StartsWith(m_currentLine, "joints {"))
					{
						UnrecognizedLine();
						break;
					}
					#endif

					if (!ParseJoints())
					{
						Error("Failed to parse joints");
						return false;
					}
					break;

				case 'm': // mesh
				{
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					if (!StartsWith(m_currentLine, "mesh {"))
					{
						UnrecognizedLine();
						break;
					}
					#endif

					if (m_meshIndex >= m_meshes.size())
					{
						#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						Warning("More meshes than registred");
						#endif

						m_meshes.emplace_back();
					}

					if (!ParseMesh())
					{
						NazaraError("Failed to parse mesh");
						return false;
					}

					m_meshIndex++;
					break;
				}

				case 'n': // num[Frames/Joints]
				{
					unsigned int count;
					if (std::sscanf(&m_currentLine[0], "numJoints %u", &count) == 1)
					{
						#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						if (!m_joints.empty())
							Warning("Joint count is already defined");
						#endif

						m_joints.resize(count);
					}
					else if (std::sscanf(&m_currentLine[0], "numMeshes %u", &count) == 1)
					{
						#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						if (!m_meshes.empty())
							Warning("Mesh count is already defined");
						#endif

						m_meshes.resize(count);
					}
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					else
						UnrecognizedLine();
					#endif
					break;
				}

				default:
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					UnrecognizedLine();
					#endif
					break;
			}
		}

		return true;
	}

	bool MD5MeshParser::Advance(bool required)
	{
		if (!m_keepLastLine)
		{
			do
			{
				if (m_stream.EndOfStream())
				{
					if (required)
						Error("Incomplete MD5 file");

					return false;
				}

				m_lineCount++;

				m_currentLine = m_stream.ReadLine();

				if (std::size_t p = m_currentLine.find("//"); p != m_currentLine.npos)
				{
					if (p > 0)
						m_currentLine = m_currentLine.substr(0, p - 1);
					else
						m_currentLine.clear();
				}

				// Trim left
				m_currentLine.erase(m_currentLine.begin(), std::find_if(m_currentLine.begin(), m_currentLine.end(), [](char c)
				{
					return !std::isspace(c);
				}));

				if (m_currentLine.empty())
					continue;
			}
			while (m_currentLine.empty());
		}
		else
			m_keepLastLine = false;

		return true;
	}

	void MD5MeshParser::Error(const std::string& message)
	{
		NazaraError(message + " at line #" + std::to_string(m_lineCount));
	}

	bool MD5MeshParser::ParseJoints()
	{
		std::size_t jointCount = m_joints.size();
		if (jointCount == 0)
		{
			Error("Joint count is invalid or missing");
			return false;
		}

		for (std::size_t i = 0; i < jointCount; ++i)
		{
			if (!Advance())
				return false;

			std::size_t pos = m_currentLine.find(' ');
			if (pos == std::string::npos)
			{
				UnrecognizedLine(true);
				return false;
			}

			if (pos >= 64)
			{
				NazaraError("Joint name is too long (>= 64 characters)");
				return false;
			}

			char name[64];
			if (std::sscanf(&m_currentLine[0], "%63s %d ( %f %f %f ) ( %f %f %f )", &name[0], &m_joints[i].parent,
			                                                                        &m_joints[i].bindPos.x, &m_joints[i].bindPos.y, &m_joints[i].bindPos.z,
			                                                                        &m_joints[i].bindOrient.x, &m_joints[i].bindOrient.y, &m_joints[i].bindOrient.z) != 8)
			{
				UnrecognizedLine(true);
				return false;
			}

			m_joints[i].name = Trim(name, '"');

			Int32 parent = m_joints[i].parent;
			if (parent >= 0)
			{
				if (static_cast<std::size_t>(parent) >= jointCount)
				{
					Error("Joint's parent is out of bounds (" + std::to_string(parent) + " >= " + std::to_string(jointCount) + ')');
					return false;
				}
			}

			m_joints[i].bindOrient.ComputeW(); // On calcule la composante W
		}

		if (!Advance())
			return false;

		if (m_currentLine != "}")
		{
			#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
			Warning("Hierarchy braces closing not found");
			#endif

			// On tente de survivre à l'erreur
			m_keepLastLine = true;
		}

		return true;
	}

	bool MD5MeshParser::ParseMesh()
	{
		bool finished = false;
		while (!finished && Advance(false))
		{
			switch (m_currentLine[0])
			{
				case '}':
					finished = true;
					break;

				case 's': // shader
				{
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					if (!StartsWith(m_currentLine, "shader "))
					{
						UnrecognizedLine();
						break;
					}
					#endif

					std::string_view shader = m_currentLine;
					shader = shader.substr(7);
					if (shader.empty())
					{
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						UnrecognizedLine();
#endif
						break;
					}

					if (shader.front() == '"')
						shader.remove_prefix(1);

					if (shader.empty())
					{
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
						UnrecognizedLine();
#endif
						break;
					}

					if (shader.back() == '"')
						shader.remove_suffix(1);

					m_meshes[m_meshIndex].shader = shader;
					break;
				}

				case 'n': // num[tris/verts]
				{
					unsigned int count;
					if (std::sscanf(&m_currentLine[0], "numtris %u", &count) == 1)
					{
						m_meshes[m_meshIndex].triangles.resize(count);
						for (unsigned int i = 0; i < count; ++i)
						{
							if (!Advance())
								return false;

							Triangle& triangle = m_meshes[m_meshIndex].triangles[i];
							unsigned int index;
							if (std::sscanf(&m_currentLine[0], "tri %u %u %u %u", &index, &triangle.x, &triangle.y, &triangle.z) != 4)
							{
								UnrecognizedLine(true);
								return false;
							}

							if (index != i)
							{
								Error("Unexpected triangle index (expected " + std::to_string(i) + ", got " + std::to_string(index) + ')');
								return false;
							}
						}
					}
					else if (std::sscanf(&m_currentLine[0], "numverts %u", &count) == 1)
					{
						m_meshes[m_meshIndex].vertices.resize(count);
						for (unsigned int i = 0; i < count; ++i)
						{
							if (!Advance())
								return false;

							Vertex& vertex = m_meshes[m_meshIndex].vertices[i];
							unsigned int index;
							if (std::sscanf(&m_currentLine[0], "vert %u ( %f %f ) %u %u", &index, &vertex.uv.x, &vertex.uv.y, &vertex.startWeight, &vertex.weightCount) != 5)
							{
								UnrecognizedLine(true);
								return false;
							}

							if (index != i)
							{
								Error("Unexpected vertex index (expected " + std::to_string(i) + ", got " + std::to_string(index) + ')');
								return false;
							}
						}
					}
					else if (std::sscanf(&m_currentLine[0], "numweights %u", &count) == 1)
					{
						m_meshes[m_meshIndex].weights.resize(count);
						for (unsigned int i = 0; i < count; ++i)
						{
							if (!Advance())
								return false;

							Weight& weight = m_meshes[m_meshIndex].weights[i];
							unsigned int index;
							if (std::sscanf(&m_currentLine[0], "weight %u %u %f ( %f %f %f )", &index, &weight.joint, &weight.bias,
							                                                                   &weight.pos.x, &weight.pos.y, &weight.pos.z) != 6)
							{
								UnrecognizedLine(true);
								return false;
							}

							if (index != i)
							{
								Error("Unexpected weight index (expected " + std::to_string(i) + ", got " + std::to_string(index) + ')');
								return false;
							}
						}
					}
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					else
						UnrecognizedLine();
					#endif
					break;
				}

				default:
					#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
					UnrecognizedLine();
					#endif
					break;
			}
		}

		if (m_meshes[m_meshIndex].triangles.empty())
		{
			NazaraError("Mesh has no triangles");
			return false;
		}

		if (m_meshes[m_meshIndex].vertices.empty())
		{
			NazaraError("Mesh has no vertices");
			return false;
		}

		if (m_meshes[m_meshIndex].weights.empty())
		{
			NazaraError("Mesh has no weights");
			return false;
		}

		#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
		if (!finished)
			Warning("Mesh braces closing not found");
		#endif

		return true;
	}

	void MD5MeshParser::Warning(const std::string& message)
	{
		NazaraWarning(message + " at line #" + std::to_string(m_lineCount));
	}

	void MD5MeshParser::UnrecognizedLine(bool error)
	{
		std::string message = "Unrecognized \"" + m_currentLine + '"';

		if (error)
			Error(message);
		else
			Warning(message);
}
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2020-present ScyllaDB
 */

/*
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */
#include "service/raft/raft_group_registry.hh"
#include "service/raft/raft_rpc.hh"
#include "service/raft/raft_gossip_failure_detector.hh"
#include "message/messaging_service.hh"
#include "serializer_impl.hh"
#include "idl/raft.dist.hh"
#include "gms/feature_service.hh"
#include "gms/gossiper.hh"

#include <seastar/core/coroutine.hh>

namespace service {

logging::logger rslog("raft_group_registry");

raft_group_registry::raft_group_registry(bool is_enabled, netw::messaging_service& ms, gms::gossiper& gossiper, gms::feature_service& feat)
    : _is_enabled(is_enabled)
    , _ms(ms)
    , _fd(make_shared<raft_gossip_failure_detector>(gossiper, _srv_address_mappings))
    , _raft_support_listener(feat.cluster_supports_raft_cluster_mgmt().when_enabled([this, &feat, &gossiper] {
        // If the `USES_RAFT_CLUSTER_MANAGEMENT` feature was already enabled, do nothing.
        //
        // This can happen if the current node is either a fresh node in an empty cluster
        // or a restarting node, which is already part of an existing group0.
        if (!_is_enabled || feat.cluster_uses_raft_cluster_mgmt()) {
            return;
        }
        // When the cluster fully supports raft-based cluster management,
        // we can re-enable support for the second gossip feature to trigger
        // actual use of raft-based cluster management procedures.
        feat.support(gms::features::USES_RAFT_CLUSTER_MANAGEMENT).get();
        // When the supported feature set is dynamically extended, re-advertise it through the gossip.
        gossiper.add_local_application_state(gms::application_state::SUPPORTED_FEATURES,
            gms::versioned_value::supported_features(feat.supported_feature_set())).get();
    }))
{
}

void raft_group_registry::init_rpc_verbs() {
    auto handle_raft_rpc = [this] (
            const rpc::client_info& cinfo,
            const raft::group_id& gid, raft::server_id from, raft::server_id dst, auto handler) {
        return container().invoke_on(shard_for_group(gid),
                [addr = netw::messaging_service::get_source(cinfo).addr, from, dst, gid, handler] (raft_group_registry& self) mutable {
            // Update the address mappings for the rpc module
            // in case the sender is encountered for the first time
            auto& rpc = self.get_rpc(gid);
            // The address learnt from a probably unknown server should
            // eventually expire
            self._srv_address_mappings.set(from, std::move(addr), true);
            // Execute the actual message handling code
            return handler(rpc);
        });
    };

    ser::raft_rpc_verbs::register_raft_send_snapshot(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::install_snapshot snp) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, snp = std::move(snp)] (raft_rpc& rpc) mutable {
            return rpc.apply_snapshot(std::move(from), std::move(snp));
        });
    });

    ser::raft_rpc_verbs::register_raft_append_entries(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
           raft::group_id gid, raft::server_id from, raft::server_id dst, raft::append_request append_request) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, append_request = std::move(append_request)] (raft_rpc& rpc) mutable {
            rpc.append_entries(std::move(from), std::move(append_request));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_append_entries_reply(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::append_reply reply) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, reply = std::move(reply)] (raft_rpc& rpc) mutable {
            rpc.append_entries_reply(std::move(from), std::move(reply));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_vote_request(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::vote_request vote_request) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, vote_request] (raft_rpc& rpc) mutable {
            rpc.request_vote(std::move(from), std::move(vote_request));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_vote_reply(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::vote_reply vote_reply) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, vote_reply] (raft_rpc& rpc) mutable {
            rpc.request_vote_reply(std::move(from), std::move(vote_reply));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_timeout_now(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::timeout_now timeout_now) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, timeout_now] (raft_rpc& rpc) mutable {
            rpc.timeout_now_request(std::move(from), std::move(timeout_now));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_read_quorum(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::read_quorum read_quorum) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, read_quorum] (raft_rpc& rpc) mutable {
            rpc.read_quorum_request(std::move(from), std::move(read_quorum));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_read_quorum_reply(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::read_quorum_reply read_quorum_reply) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, read_quorum_reply] (raft_rpc& rpc) mutable {
            rpc.read_quorum_reply(std::move(from), std::move(read_quorum_reply));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_execute_read_barrier_on_leader(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from] (raft_rpc& rpc) mutable {
            return rpc.execute_read_barrier(from);
        });
    });

    ser::raft_rpc_verbs::register_raft_add_entry(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::command cmd) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, cmd = std::move(cmd)] (raft_rpc& rpc) mutable {
            return rpc.execute_add_entry(from, std::move(cmd));
        });
    });

    ser::raft_rpc_verbs::register_raft_modify_config(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst,
            std::vector<raft::server_address> add, std::vector<raft::server_id> del) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst,
            [from, add = std::move(add), del = std::move(del)] (raft_rpc& rpc) mutable {

            return rpc.execute_modify_config(from, std::move(add), std::move(del));
        });
    });
}

future<> raft_group_registry::uninit_rpc_verbs() {
    return when_all_succeed(
        ser::raft_rpc_verbs::unregister_raft_send_snapshot(&_ms),
        ser::raft_rpc_verbs::unregister_raft_append_entries(&_ms),
        ser::raft_rpc_verbs::unregister_raft_append_entries_reply(&_ms),
        ser::raft_rpc_verbs::unregister_raft_vote_request(&_ms),
        ser::raft_rpc_verbs::unregister_raft_vote_reply(&_ms),
        ser::raft_rpc_verbs::unregister_raft_timeout_now(&_ms),
        ser::raft_rpc_verbs::unregister_raft_read_quorum(&_ms),
        ser::raft_rpc_verbs::unregister_raft_read_quorum_reply(&_ms),
        ser::raft_rpc_verbs::unregister_raft_execute_read_barrier_on_leader(&_ms),
        ser::raft_rpc_verbs::unregister_raft_add_entry(&_ms),
        ser::raft_rpc_verbs::unregister_raft_modify_config(&_ms)
    ).discard_result();
}

future<> raft_group_registry::stop_servers() noexcept {
    gate g;
    for (auto it = _servers.begin(); it != _servers.end(); it = _servers.erase(it)) {
        auto abort_server = it->second.server->abort();
        // discarded future is waited via g.close()
        (void)std::move(abort_server).handle_exception([rsfg = std::move(it->second), gh = g.hold()] (std::exception_ptr ex) {
            rslog.warn("Failed to abort raft group server {}: {}", rsfg.gid, ex);
        });
    }
    co_await g.close();
}

seastar::future<> raft_group_registry::start() {
    if (!_is_enabled) {
        co_return;
    }
    // Once a Raft server starts, it soon times out
    // and starts an election, so RPC must be ready by
    // then to send VoteRequest messages.
    co_return init_rpc_verbs();
}

seastar::future<> raft_group_registry::stop() {
    if (!_is_enabled) {
        co_return;
    }
    co_await drain_on_shutdown();
    co_await uninit_rpc_verbs();
}

seastar::future<> raft_group_registry::drain_on_shutdown() noexcept {
    return stop_servers();
}

raft_server_for_group& raft_group_registry::server_for_group(raft::group_id gid) {
    auto it = _servers.find(gid);
    if (it == _servers.end()) {
        throw raft_group_not_found(gid);
    }
    return it->second;
}

raft_rpc& raft_group_registry::get_rpc(raft::group_id gid) {
    return server_for_group(gid).rpc;
}

raft::server& raft_group_registry::get_server(raft::group_id gid) {
    return *(server_for_group(gid).server);
}

raft::server& raft_group_registry::group0() {
    return *(server_for_group(*_group0_id).server);
}

future<> raft_group_registry::start_server_for_group(raft_server_for_group new_grp) {
    auto gid = new_grp.gid;

    if (_servers.contains(gid)) {
        on_internal_error(rslog, format("Attempt to add the second instance of raft server with the same gid={}", gid));
    }

    try {
        // start the server instance prior to arming the ticker timer.
        // By the time the tick() is executed the server should already be initialized.
        co_await new_grp.server->start();
        new_grp.server->register_metrics();
    } catch (...) {
        on_internal_error(rslog, std::current_exception());
    }

    std::exception_ptr ex;
    raft::server& server = *new_grp.server;

    try {
        auto [it, inserted] = _servers.emplace(std::move(gid), std::move(new_grp));

        if (_servers.size() == 1 && this_shard_id() == 0) {
            _group0_id = gid;
        }

        it->second.ticker->arm_periodic(raft_tick_interval);
    } catch (...) {
        ex = std::current_exception();
    }

    if (ex) {
        co_await server.abort();
        std::rethrow_exception(ex);
    }
}

unsigned raft_group_registry::shard_for_group(const raft::group_id& gid) const {
    return 0; // schema raft server is always owned by shard 0
}

} // end of namespace service
<commit_msg>raft_group_registry: update gossiper state only on shard 0<commit_after>/*
 * Copyright (C) 2020-present ScyllaDB
 */

/*
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */
#include "service/raft/raft_group_registry.hh"
#include "service/raft/raft_rpc.hh"
#include "service/raft/raft_gossip_failure_detector.hh"
#include "message/messaging_service.hh"
#include "serializer_impl.hh"
#include "idl/raft.dist.hh"
#include "gms/feature_service.hh"
#include "gms/gossiper.hh"

#include <seastar/core/coroutine.hh>

namespace service {

logging::logger rslog("raft_group_registry");

raft_group_registry::raft_group_registry(bool is_enabled, netw::messaging_service& ms, gms::gossiper& gossiper, gms::feature_service& feat)
    : _is_enabled(is_enabled)
    , _ms(ms)
    , _fd(make_shared<raft_gossip_failure_detector>(gossiper, _srv_address_mappings))
    , _raft_support_listener(feat.cluster_supports_raft_cluster_mgmt().when_enabled([this, &feat, &gossiper] {
        // If the `USES_RAFT_CLUSTER_MANAGEMENT` feature was already enabled, do nothing.
        //
        // This can happen if the current node is either a fresh node in an empty cluster
        // or a restarting node, which is already part of an existing group0.
        if (!_is_enabled || feat.cluster_uses_raft_cluster_mgmt()) {
            return;
        }
        // When the cluster fully supports raft-based cluster management,
        // we can re-enable support for the second gossip feature to trigger
        // actual use of raft-based cluster management procedures.
        feat.support(gms::features::USES_RAFT_CLUSTER_MANAGEMENT).get();
        if (this_shard_id() == 0) {
            // When the supported feature set is dynamically extended, re-advertise it through the gossip.
            gossiper.add_local_application_state(gms::application_state::SUPPORTED_FEATURES,
                gms::versioned_value::supported_features(feat.supported_feature_set())).get();
        }
    }))
{
}

void raft_group_registry::init_rpc_verbs() {
    auto handle_raft_rpc = [this] (
            const rpc::client_info& cinfo,
            const raft::group_id& gid, raft::server_id from, raft::server_id dst, auto handler) {
        return container().invoke_on(shard_for_group(gid),
                [addr = netw::messaging_service::get_source(cinfo).addr, from, dst, gid, handler] (raft_group_registry& self) mutable {
            // Update the address mappings for the rpc module
            // in case the sender is encountered for the first time
            auto& rpc = self.get_rpc(gid);
            // The address learnt from a probably unknown server should
            // eventually expire
            self._srv_address_mappings.set(from, std::move(addr), true);
            // Execute the actual message handling code
            return handler(rpc);
        });
    };

    ser::raft_rpc_verbs::register_raft_send_snapshot(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::install_snapshot snp) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, snp = std::move(snp)] (raft_rpc& rpc) mutable {
            return rpc.apply_snapshot(std::move(from), std::move(snp));
        });
    });

    ser::raft_rpc_verbs::register_raft_append_entries(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
           raft::group_id gid, raft::server_id from, raft::server_id dst, raft::append_request append_request) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, append_request = std::move(append_request)] (raft_rpc& rpc) mutable {
            rpc.append_entries(std::move(from), std::move(append_request));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_append_entries_reply(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::append_reply reply) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, reply = std::move(reply)] (raft_rpc& rpc) mutable {
            rpc.append_entries_reply(std::move(from), std::move(reply));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_vote_request(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::vote_request vote_request) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, vote_request] (raft_rpc& rpc) mutable {
            rpc.request_vote(std::move(from), std::move(vote_request));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_vote_reply(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::vote_reply vote_reply) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, vote_reply] (raft_rpc& rpc) mutable {
            rpc.request_vote_reply(std::move(from), std::move(vote_reply));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_timeout_now(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::timeout_now timeout_now) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, timeout_now] (raft_rpc& rpc) mutable {
            rpc.timeout_now_request(std::move(from), std::move(timeout_now));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_read_quorum(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::read_quorum read_quorum) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, read_quorum] (raft_rpc& rpc) mutable {
            rpc.read_quorum_request(std::move(from), std::move(read_quorum));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_read_quorum_reply(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::read_quorum_reply read_quorum_reply) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, read_quorum_reply] (raft_rpc& rpc) mutable {
            rpc.read_quorum_reply(std::move(from), std::move(read_quorum_reply));
            return netw::messaging_service::no_wait();
        });
    });

    ser::raft_rpc_verbs::register_raft_execute_read_barrier_on_leader(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from] (raft_rpc& rpc) mutable {
            return rpc.execute_read_barrier(from);
        });
    });

    ser::raft_rpc_verbs::register_raft_add_entry(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::command cmd) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, cmd = std::move(cmd)] (raft_rpc& rpc) mutable {
            return rpc.execute_add_entry(from, std::move(cmd));
        });
    });

    ser::raft_rpc_verbs::register_raft_modify_config(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst,
            std::vector<raft::server_address> add, std::vector<raft::server_id> del) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst,
            [from, add = std::move(add), del = std::move(del)] (raft_rpc& rpc) mutable {

            return rpc.execute_modify_config(from, std::move(add), std::move(del));
        });
    });
}

future<> raft_group_registry::uninit_rpc_verbs() {
    return when_all_succeed(
        ser::raft_rpc_verbs::unregister_raft_send_snapshot(&_ms),
        ser::raft_rpc_verbs::unregister_raft_append_entries(&_ms),
        ser::raft_rpc_verbs::unregister_raft_append_entries_reply(&_ms),
        ser::raft_rpc_verbs::unregister_raft_vote_request(&_ms),
        ser::raft_rpc_verbs::unregister_raft_vote_reply(&_ms),
        ser::raft_rpc_verbs::unregister_raft_timeout_now(&_ms),
        ser::raft_rpc_verbs::unregister_raft_read_quorum(&_ms),
        ser::raft_rpc_verbs::unregister_raft_read_quorum_reply(&_ms),
        ser::raft_rpc_verbs::unregister_raft_execute_read_barrier_on_leader(&_ms),
        ser::raft_rpc_verbs::unregister_raft_add_entry(&_ms),
        ser::raft_rpc_verbs::unregister_raft_modify_config(&_ms)
    ).discard_result();
}

future<> raft_group_registry::stop_servers() noexcept {
    gate g;
    for (auto it = _servers.begin(); it != _servers.end(); it = _servers.erase(it)) {
        auto abort_server = it->second.server->abort();
        // discarded future is waited via g.close()
        (void)std::move(abort_server).handle_exception([rsfg = std::move(it->second), gh = g.hold()] (std::exception_ptr ex) {
            rslog.warn("Failed to abort raft group server {}: {}", rsfg.gid, ex);
        });
    }
    co_await g.close();
}

seastar::future<> raft_group_registry::start() {
    if (!_is_enabled) {
        co_return;
    }
    // Once a Raft server starts, it soon times out
    // and starts an election, so RPC must be ready by
    // then to send VoteRequest messages.
    co_return init_rpc_verbs();
}

seastar::future<> raft_group_registry::stop() {
    if (!_is_enabled) {
        co_return;
    }
    co_await drain_on_shutdown();
    co_await uninit_rpc_verbs();
}

seastar::future<> raft_group_registry::drain_on_shutdown() noexcept {
    return stop_servers();
}

raft_server_for_group& raft_group_registry::server_for_group(raft::group_id gid) {
    auto it = _servers.find(gid);
    if (it == _servers.end()) {
        throw raft_group_not_found(gid);
    }
    return it->second;
}

raft_rpc& raft_group_registry::get_rpc(raft::group_id gid) {
    return server_for_group(gid).rpc;
}

raft::server& raft_group_registry::get_server(raft::group_id gid) {
    return *(server_for_group(gid).server);
}

raft::server& raft_group_registry::group0() {
    return *(server_for_group(*_group0_id).server);
}

future<> raft_group_registry::start_server_for_group(raft_server_for_group new_grp) {
    auto gid = new_grp.gid;

    if (_servers.contains(gid)) {
        on_internal_error(rslog, format("Attempt to add the second instance of raft server with the same gid={}", gid));
    }

    try {
        // start the server instance prior to arming the ticker timer.
        // By the time the tick() is executed the server should already be initialized.
        co_await new_grp.server->start();
        new_grp.server->register_metrics();
    } catch (...) {
        on_internal_error(rslog, std::current_exception());
    }

    std::exception_ptr ex;
    raft::server& server = *new_grp.server;

    try {
        auto [it, inserted] = _servers.emplace(std::move(gid), std::move(new_grp));

        if (_servers.size() == 1 && this_shard_id() == 0) {
            _group0_id = gid;
        }

        it->second.ticker->arm_periodic(raft_tick_interval);
    } catch (...) {
        ex = std::current_exception();
    }

    if (ex) {
        co_await server.abort();
        std::rethrow_exception(ex);
    }
}

unsigned raft_group_registry::shard_for_group(const raft::group_id& gid) const {
    return 0; // schema raft server is always owned by shard 0
}

} // end of namespace service
<|endoftext|>


<commit_before>#include "acmacs-base/argv.hh"
// #include "acmacs-base/range-v3.hh"
#include "acmacs-base/enumerate.hh"
#include "acmacs-base/string-join.hh"
#include "acmacs-chart-2/factory-import.hh"
#include "acmacs-chart-2/chart.hh"
#include "hidb-5/hidb-set.hh"
#include "hidb-5/hidb.hh"
#include "acmacs-whocc/log.hh"

// ----------------------------------------------------------------------

using namespace acmacs::argv;
struct Options : public argv
{
    Options(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) : argv() { parse(a_argc, a_argv, on_err); }

    option<str_array> verbose{*this, 'v', "verbose", desc{"comma separated list (or multiple switches) of log enablers"}};

    argument<str_array> tables{*this, arg_name{".ace"}, mandatory};
};

int main(int argc, char* const argv[])
{
    using namespace std::string_view_literals;
    int exit_code = 0;
    try {
        Options opt(argc, argv);
        acmacs::log::enable(opt.verbose);

        const auto show_matches = [](const auto& hidb, const auto& hidb_ag_sr, size_t no, const auto& ag_sr) {
            const auto make_field = [](const auto& value, const auto& to_compare) {
                if (!value.empty() && value == to_compare)
                    return fmt::format("<{}>", value);
                else
                    return fmt::format("{}", value);
            };

            constexpr auto is_antigen = std::is_same_v<std::decay_t<decltype(ag_sr)>, acmacs::chart::Antigen>;
            const auto full_name = ag_sr.full_name();
            const auto reassortant = ag_sr.reassortant();
            const auto annotations = acmacs::string::join(acmacs::string::join_space, ag_sr.annotations());
            if constexpr (is_antigen)
                fmt::print("AG");
            else
                fmt::print("SR");
            fmt::print("{:3d} \"{}\"\n", no, full_name);

            std::vector<std::pair<std::string, fmt::memory_buffer>> hidb_variants;
            bool full_name_match_present{false};
            for (const auto& [hidb_antigen, hidb_index] : hidb_ag_sr) {
                std::string hidb_full_name;
                if constexpr (is_antigen)
                    hidb_full_name = acmacs::string::join(acmacs::string::join_space, hidb_antigen->name(), acmacs::string::join(acmacs::string::join_space, hidb_antigen->annotations()),
                                                          hidb_antigen->reassortant(), hidb_antigen->passage());
                else
                    hidb_full_name = acmacs::string::join(acmacs::string::join_space, hidb_antigen->name(), acmacs::string::join(acmacs::string::join_space, hidb_antigen->annotations()),
                                                          hidb_antigen->reassortant(), hidb_antigen->serum_id());
                full_name_match_present |= hidb_full_name == full_name;
                auto& variant = hidb_variants.emplace_back(std::move(hidb_full_name), fmt::memory_buffer{});
                // mark matching matching annotations, reassortant, passage, serum_id
                if constexpr (is_antigen)
                    fmt::format_to(variant.second, "{}",
                                   acmacs::string::join(acmacs::string::join_space,
                                                        /* hidb_antigen->name(), */ make_field(acmacs::string::join(acmacs::string::join_space, hidb_antigen->annotations()), annotations),
                                                        make_field(hidb_antigen->reassortant(), reassortant), make_field(hidb_antigen->passage(), ag_sr.passage())));
                else
                    fmt::format_to(variant.second, "{}",
                                   acmacs::string::join(acmacs::string::join_space,
                                                        /* hidb_antigen->name(), */ make_field(acmacs::string::join(acmacs::string::join_space, hidb_antigen->annotations()), annotations),
                                                        make_field(hidb_antigen->reassortant(), reassortant), make_field(hidb_antigen->serum_id(), ag_sr.serum_id())));
                fmt::format_to(variant.second, "\n");
                const auto by_lab_assay = hidb.tables(*hidb_antigen, hidb::lab_assay_rbc_table_t::recent_first);
                for (auto entry : by_lab_assay) {
                    fmt::format_to(variant.second, "        [{} {}] ({})", entry.lab, acmacs::chart::assay_rbc_short(entry.assay, entry.rbc), entry.tables.size());
                    for (auto table : entry.tables)
                        fmt::format_to(variant.second, " {}", table->date());
                    fmt::format_to(variant.second, "\n");
                }
            }
            std::sort(std::begin(hidb_variants), std::end(hidb_variants), [&full_name](const auto& e1, const auto& e2) {
                const auto f1 = e1.first == full_name, f2 = e2.first == full_name;
                if (f1 && !f2)
                    return true;
                else if (!f1 && f2)
                    return false;
                else
                    return e1.first < e2.first;
            });
            if (!full_name_match_present)
                hidb_variants.emplace(hidb_variants.begin(), "*** New ***", fmt::memory_buffer{});
            for (const auto& variant : hidb_variants) {
                if (full_name_match_present) {
                    fmt::print("  + ");
                    full_name_match_present = false;
                }
                else
                    fmt::print("    ");
                fmt::print("{}", fmt::to_string(variant.second));
            }
        };

        fmt::print("# -*- Mode: eu-whocc-scan2; -*-\n\n");
        for (const auto& filename : *opt.tables) {
            auto chart = acmacs::chart::import_from_file(filename);
            fmt::print(">> {}\n\n", chart->make_name());
            const auto& hidb = hidb::get(chart->info()->virus_type(acmacs::chart::Info::Compute::Yes));
            auto tables = hidb.tables();

            auto antigens = chart->antigens();
            for (auto [ag_no, antigen] : acmacs::enumerate(*antigens)) {
                if (!antigen->distinct()) {
                    const auto hidb_antigens = hidb.antigens()->find(antigen->name(), hidb::fix_location::no);
                    if (!hidb_antigens.empty())
                        show_matches(hidb, hidb_antigens, ag_no, *antigen);
                }
            }
            fmt::print("\n\n");

            auto sera = chart->sera();
            for (auto [sr_no, serum] : acmacs::enumerate(*sera)) {
                const auto hidb_sera = hidb.sera()->find(serum->name(), hidb::fix_location::no);
                // if (!hidb_sera.empty())
                show_matches(hidb, hidb_sera, sr_no, *serum);
            }
            fmt::print("\n\n");
            fmt::print("> ====================================================================================================\n\n");
        }
    }
    catch (std::exception& err) {
        AD_ERROR("{}", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>whocc-check-new-tables<commit_after>#include "acmacs-base/argv.hh"
// #include "acmacs-base/range-v3.hh"
#include "acmacs-base/enumerate.hh"
#include "acmacs-base/string-join.hh"
#include "acmacs-chart-2/factory-import.hh"
#include "acmacs-chart-2/chart.hh"
#include "hidb-5/hidb-set.hh"
#include "hidb-5/hidb.hh"
#include "acmacs-whocc/log.hh"

// ----------------------------------------------------------------------

using namespace acmacs::argv;
struct Options : public argv
{
    Options(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) : argv() { parse(a_argc, a_argv, on_err); }

    option<str_array> verbose{*this, 'v', "verbose", desc{"comma separated list (or multiple switches) of log enablers"}};

    argument<str_array> tables{*this, arg_name{".ace"}, mandatory};
};

int main(int argc, char* const argv[])
{
    using namespace std::string_view_literals;
    int exit_code = 0;
    try {
        Options opt(argc, argv);
        acmacs::log::enable(opt.verbose);

        const auto show_matches = [](const auto& hidb, const auto& hidb_ag_sr, size_t no, const auto& ag_sr) {
            const auto make_field = [](const auto& value, const auto& to_compare) {
                if (!value.empty() && value == to_compare)
                    return fmt::format("<{}>", value);
                else
                    return fmt::format("{}", value);
            };

            constexpr auto is_antigen = std::is_same_v<std::decay_t<decltype(ag_sr)>, acmacs::chart::Antigen>;
            const auto full_name = ag_sr.full_name();
            const auto reassortant = ag_sr.reassortant();
            const auto annotations = acmacs::string::join(acmacs::string::join_space, ag_sr.annotations());
            if constexpr (is_antigen)
                fmt::print("AG");
            else
                fmt::print("SR");
            fmt::print("{:3d} \"{}\"\n", no, full_name);

            std::vector<std::pair<std::string, fmt::memory_buffer>> hidb_variants;
            bool full_name_match_present{false};
            for (const auto& [hidb_antigen, hidb_index] : hidb_ag_sr) {
                std::string hidb_full_name;
                if constexpr (is_antigen)
                    hidb_full_name = acmacs::string::join(acmacs::string::join_space, hidb_antigen->name(), acmacs::string::join(acmacs::string::join_space, hidb_antigen->annotations()),
                                                          hidb_antigen->reassortant(), hidb_antigen->passage());
                else
                    hidb_full_name = acmacs::string::join(acmacs::string::join_space, hidb_antigen->name(), acmacs::string::join(acmacs::string::join_space, hidb_antigen->annotations()),
                                                          hidb_antigen->reassortant(), hidb_antigen->serum_id());
                full_name_match_present |= hidb_full_name == full_name;
                auto& variant = hidb_variants.emplace_back(std::move(hidb_full_name), fmt::memory_buffer{});
                // mark matching matching annotations, reassortant, passage, serum_id
                if constexpr (is_antigen)
                    fmt::format_to(variant.second, "{}",
                                   acmacs::string::join(acmacs::string::join_space,
                                                        /* hidb_antigen->name(), */ make_field(acmacs::string::join(acmacs::string::join_space, hidb_antigen->annotations()), annotations),
                                                        make_field(hidb_antigen->reassortant(), reassortant), make_field(hidb_antigen->passage(), ag_sr.passage())));
                else
                    fmt::format_to(variant.second, "{}",
                                   acmacs::string::join(acmacs::string::join_space,
                                                        /* hidb_antigen->name(), */ make_field(acmacs::string::join(acmacs::string::join_space, hidb_antigen->annotations()), annotations),
                                                        make_field(hidb_antigen->reassortant(), reassortant), make_field(hidb_antigen->serum_id(), ag_sr.serum_id())));
                fmt::format_to(variant.second, "\n");
                const auto by_lab_assay = hidb.tables(*hidb_antigen, hidb::lab_assay_rbc_table_t::recent_first);
                for (auto entry : by_lab_assay) {
                    fmt::format_to(variant.second, "        [{} {}] ({})", entry.lab, acmacs::chart::assay_rbc_short(entry.assay, entry.rbc), entry.tables.size());
                    for (auto table : entry.tables)
                        fmt::format_to(variant.second, " {}", table->date());
                    fmt::format_to(variant.second, "\n");
                }
            }
            std::sort(std::begin(hidb_variants), std::end(hidb_variants), [&full_name](const auto& e1, const auto& e2) {
                const auto f1 = e1.first == full_name, f2 = e2.first == full_name;
                if (f1 && !f2)
                    return true;
                else if (!f1 && f2)
                    return false;
                else
                    return e1.first < e2.first;
            });
            if (!full_name_match_present) {
                const auto new_var = hidb_variants.emplace(hidb_variants.begin(), "*** New ***", fmt::memory_buffer{});
                fmt::format_to(new_var->second, "*** New ***\n");
            }
            for (const auto& variant : hidb_variants) {
                if (full_name_match_present) {
                    fmt::print("  + ");
                    full_name_match_present = false;
                }
                else
                    fmt::print("    ");
                fmt::print("{}", fmt::to_string(variant.second));
            }
        };

        fmt::print("# -*- Mode: eu-whocc-scan2; -*-\n\n");
        for (const auto& filename : *opt.tables) {
            auto chart = acmacs::chart::import_from_file(filename);
            fmt::print(">> {}\n\n", chart->make_name());
            const auto& hidb = hidb::get(chart->info()->virus_type(acmacs::chart::Info::Compute::Yes));
            auto tables = hidb.tables();

            auto antigens = chart->antigens();
            for (auto [ag_no, antigen] : acmacs::enumerate(*antigens)) {
                if (!antigen->distinct()) {
                    const auto hidb_antigens = hidb.antigens()->find(antigen->name(), hidb::fix_location::no);
                    if (!hidb_antigens.empty())
                        show_matches(hidb, hidb_antigens, ag_no, *antigen);
                }
            }
            fmt::print("\n\n");

            auto sera = chart->sera();
            for (auto [sr_no, serum] : acmacs::enumerate(*sera)) {
                const auto hidb_sera = hidb.sera()->find(serum->name(), hidb::fix_location::no);
                // if (!hidb_sera.empty())
                show_matches(hidb, hidb_sera, sr_no, *serum);
            }
            fmt::print("\n\n");
            fmt::print("> ====================================================================================================\n\n");
        }
    }
    catch (std::exception& err) {
        AD_ERROR("{}", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>// Time:  O(logn)
// Space: O(1)

class Solution {
public:
    /**
     * @param dividend the dividend
     * @param divisor the divisor
     * @return the result
     */
    int divide(int dividend, int divisor) {
        // Handle corner case.
        if (divisor == INT_MIN) {
            return dividend == divisor ? 1 : 0;
        } else if (!divisor || divisor == -1 && dividend == INT_MIN) {
            return INT_MAX;
        } else if (divisor == 1) {
            return dividend;
        }

        bool negative = (dividend > 0) ^ (divisor > 0);
        if (dividend > 0) {
            dividend = -dividend;
        }
        if (divisor > 0) {
            divisor = -divisor;
        }

        int product = divisor;
        int idx = 1;
        while (product < 0 && idx < 32) {
            ++idx;
            product <<= 1;
        }
        idx -= 2; // Skip value of INT_MIN
        product = divisor << idx;
        int multiplier = 1 << idx;

        int res = 0;
        while (dividend <= divisor) {
            if (dividend <= product) {
                res += multiplier;
                dividend -= product;
            }
            --idx;
            product = divisor << idx;
            multiplier >>= 1;
        }
        return negative ? -res : res;
    }
};

class Solution2 {
public:
    /**
     * @param dividend the dividend
     * @param divisor the divisor
     * @return the result
     */
    int divide(int dividend, int divisor) {
        // Handle corner case.
        if (divisor == INT_MIN) {
            return dividend == divisor ? 1 : 0;
        } else if (!divisor || divisor == -1 && dividend == INT_MIN) {
            return INT_MAX;
        } else if (divisor == 1) {
            return dividend;
        }

        bool negative = (dividend > 0) ^ (divisor > 0);
        if (dividend > 0) {
            dividend = -dividend;
        }
        if (divisor > 0) {
            divisor = -divisor;
        }
        vector<int> multipliers, products;
        multipliers.push_back(1);
        products.push_back(divisor);
        int idx = 1, res = 0;
        while (products[idx - 1] < 0 && idx < 32) {
            products.push_back(divisor << idx);
            multipliers.push_back(1 << idx);
            ++idx;
        }
        idx -= 2;
        while (dividend <= divisor) {
            if (dividend <= products[idx]) {
                res += multipliers[idx];
                dividend -= products[idx];
            }
            --idx;
        }
        return negative ? -res : res;
    }
};

// Time:  O(1)
// Space: O(1)
// a / b = exp^(ln(a) - ln(b))
class Solution3 {
public:
    /**
     * @param dividend the dividend
     * @param divisor the divisor
     * @return the result
     */
    int divide(int dividend, int divisor) {
        if (dividend == 0) {
            return 0;
        }
        if (divisor == 0) {
            return INT_MAX;
        }

        long long res = exp(log(fabs(dividend)) - log(fabs(divisor)));

        if ((dividend < 0) ^ (divisor < 0)) {
            res = -res;
        }
        if (res > INT_MAX) {
            res = INT_MAX;
        }
        return res;
    }
};
<commit_msg>Update divide-two-integers.cpp<commit_after>// Time:  O(logn)
// Space: O(1)

// Only using integer type.
class Solution {
public:
    /**
     * @param dividend the dividend
     * @param divisor the divisor
     * @return the result
     */
    int divide(int dividend, int divisor) {
        // Handle corner case.
        if (divisor == INT_MIN) {
            return dividend == divisor ? 1 : 0;
        } else if (!divisor || divisor == -1 && dividend == INT_MIN) {
            return INT_MAX;
        } else if (divisor == 1) {
            return dividend;
        }

        bool negative = (dividend > 0) ^ (divisor > 0);
        if (dividend > 0) {
            dividend = -dividend;
        }
        if (divisor > 0) {
            divisor = -divisor;
        }

        int product = divisor;
        int idx = 1;
        while (product < 0 && idx < 32) {
            ++idx;
            product <<= 1;
        }
        idx -= 2; // Skip value of INT_MIN
        product = divisor << idx;
        int multiplier = 1 << idx;

        int res = 0;
        while (dividend <= divisor) {
            if (dividend <= product) {
                res += multiplier;
                dividend -= product;
            }
            --idx;
            product = divisor << idx;
            multiplier >>= 1;
        }
        return negative ? -res : res;
    }
};

// Time:  O(logn)
// Space: O(1)
// Using long long type.
class Solution2 {
public:
    /**
     * @param dividend the dividend
     * @param divisor the divisor
     * @return the result
     */
    int divide(int dividend, int divisor) {
        long long result = 0, dvd = llabs(dividend), dvs = llabs(divisor);

        long long inc = dvs;
        long long multiplier = 1;
        int i = 0;
        while (dvd >= inc) {
                inc <<= 1;
                multiplier <<= 1;
                ++i;
        }

        while (inc && i >= 0) {
                while (dvd >= inc) {
                    dvd -= inc;
                    result += multiplier;
                }
                inc >>= 1;
                multiplier >>= 1;
                --i;
        }

        if (dividend > 0 and divisor < 0 or dividend < 0 and divisor > 0) {
            return -result;
        } else {
            return min(result, static_cast<long long>(INT_MAX));
        }
    }
};

// Time:  O(1)
// Space: O(1)
// a / b = exp^(ln(a) - ln(b))
class Solution3 {
public:
    /**
     * @param dividend the dividend
     * @param divisor the divisor
     * @return the result
     */
    int divide(int dividend, int divisor) {
        if (dividend == 0) {
            return 0;
        }
        if (divisor == 0) {
            return INT_MAX;
        }

        long long res = exp(log(fabs(dividend)) - log(fabs(divisor)));

        if ((dividend < 0) ^ (divisor < 0)) {
            res = -res;
        }
        if (res > INT_MAX) {
            res = INT_MAX;
        }
        return res;
    }
};
<|endoftext|>


<commit_before>#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>

using std::cout;
using std::endl;
using std::queue;
using std::thread;
using std::mutex;
using std::unique_lock;
using std::lock_guard;
using std::condition_variable;
using std::chrono::milliseconds;
using std::this_thread::sleep_for;

class Buffer {
public:
    Buffer() {}
    void add(int num) {
        {
            unique_lock<mutex> lock(mu);
            cond.wait(lock, [this]() { return buffer_.size() != size_; });
            buffer_.emplace(num);
        }
        // Unlocking is done before notifying, to avoid waking up
        // the waiting thread only to block again.
        cond.notify_all();
    }
    int remove() {
        int back;
        {
            unique_lock<mutex> lock(mu);
            cond.wait(lock, [this]() { return buffer_.size() != 0; });
            back = buffer_.front();
            buffer_.pop();
        }
        // Unlocking is done before notifying, to avoid waking up
        // the waiting thread only to block again.
        cond.notify_all();
        return back;
    }
private:
    mutex mu;
    condition_variable cond;
    queue<int> buffer_;
    const unsigned int size_ = 5;
};

class Producer {
public:
    Producer(Buffer& buffer, mutex& cout_mu) :
             buffer_(buffer), cout_mu_(cout_mu) {}

    void run() {
        for (int i = 0; i < 100; ++i) {
            const int num = i;
            buffer_.add(i);
            {
                lock_guard<mutex> lock(cout_mu_);
                cout << "Produced: " << num << endl;
            }
            sleep_for(milliseconds(50));
        }
    }

private:
    Buffer& buffer_;
    mutex& cout_mu_;
};

class Consumer {
public:
    Consumer(Buffer& buffer, mutex& cout_mu) : buffer_(buffer), cout_mu_(cout_mu) {}
    void run() {
        for (int i = 0; i < 100; ++i) {
            int num = buffer_.remove();
            {
                lock_guard<mutex> lock(cout_mu_);
                cout << "Consumed: " << num << endl;
            }
            sleep_for(milliseconds(500));
        }
    }

private:
    Buffer& buffer_;
    mutex& cout_mu_;
};

int main() {
    Buffer b;
    mutex cout_mu;
    Producer p(b, cout_mu);
    Consumer c(b, cout_mu);

    thread producer_thread(&Producer::run, &p);
    thread consumer_thread(&Consumer::run, &c);

    producer_thread.join();
    consumer_thread.join();

    return 0;
}
<commit_msg>update<commit_after>#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>

using std::cout;
using std::endl;
using std::queue;
using std::thread;
using std::mutex;
using std::unique_lock;
using std::lock_guard;
using std::condition_variable;
using std::chrono::milliseconds;
using std::this_thread::sleep_for;

template<typename T>
class SyncQueue {
public:
    void put(const T& val) {
        unique_lock<mutex> lock{mtx};
        cond.wait(lock, [this]() { return q.size() != q_size; });
        q.emplace(val);
        cond.notify_one();
    }

    void get(T& val) {
        unique_lock<mutex> lock{mtx};
        cond.wait(lock, [this]() { return !q.empty(); });
        val = q.front();
        q.pop();
        cond.notify_one();
    }

private:
    mutex mtx;
    condition_variable cond;
    queue<T> q;
    const unsigned int q_size = 5;
};

class Producer {
public:
    Producer(SyncQueue<int>& q, mutex& cout_mtx) :
             q_(q), cout_mtx_(cout_mtx) {}

    void run() {
        for (int i = 0; i < 100; ++i) {
            const int num = i;
            q_.put(num);
            {
                lock_guard<mutex> lock(cout_mtx_);
                cout << "Produced: " << num << endl;
            }
            sleep_for(milliseconds(50));
        }
    }

private:
    SyncQueue<int>& q_;
    mutex& cout_mtx_;
};

class Consumer {
public:
    Consumer(SyncQueue<int>& q, mutex& cout_mtx) : q_(q), cout_mtx_(cout_mtx) {}
    void run() {
        for (int i = 0; i < 100; ++i) {
            int num;
            q_.get(num);
            {
                lock_guard<mutex> lock(cout_mtx_);
                cout << "Consumed: " << num << endl;
            }
            sleep_for(milliseconds(500));
        }
    }

private:
    SyncQueue<int>& q_;
    mutex& cout_mtx_;
};

int main() {
    SyncQueue<int> q;
    mutex cout_mtx;
    Producer p{q, cout_mtx};
    Consumer c{q, cout_mtx};

    thread producer_thread{&Producer::run, &p};
    thread consumer_thread{&Consumer::run, &c};

    producer_thread.join();
    consumer_thread.join();

    return 0;
}
<|endoftext|>