<commit_before>// This file is part of MLDB. Copyright 2015 Datacratic. All rights reserved.

/** static_content_handler.cc
    Jeremy Barnes, 5 March 2015
    Copyright (c) 2015 Datacratic Inc.  All rights reserved.

    Handler for static content.
*/

#include <typeinfo>
#include "mldb/server/static_content_handler.h"
#include "mldb/rest/rest_request_router.h"
#include "mldb/vfs/fs_utils.h"
#include "mldb/jml/utils/file_functions.h"
#include "mldb/ext/hoedown/src/buffer.h"
#include "mldb/ext/hoedown/src/html.h"
#include "mldb/ext/hoedown/src/document.h"
#include "mldb/ext/hoedown/src/escape.h"
#include "mldb/rest/in_process_rest_connection.h"
#include "mldb/server/mldb_server.h"
#include "mldb/core/mldb_entity.h"
#include "static_content_macro.h"
#include "mldb/base/scope.h"

using namespace std;

namespace Datacratic {
namespace MLDB {

void renderMacro(hoedown_buffer *ob,
                 const hoedown_buffer *text,
                 const hoedown_renderer_data *,
                 const MacroData & macroData)
{
    MacroContext context(&macroData, ob, text);

    string s((const char *)text->data, 2, text->size - 2);
    auto pos = s.find(' ');
    string name(s, 0, pos);
    string args(s, pos + 1);
    callMacro(context, name, args);
}

void handleNormalText (hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
{
    if (!text)
        return;

    auto state = (hoedown_html_renderer_state *)data->opaque;

    if (text->data && text->size > 2 && text->data[0] == '%' && text->data[1] == '%') {
        renderMacro(ob, text, data, *(MacroData*)state->user_data);
        return;
    }

    hoedown_escape_html(ob, text->data, text->size, 0);

}

int handleImage (hoedown_buffer *ob,
                 const hoedown_buffer *link,
                 const hoedown_buffer *title,
                 const hoedown_buffer *alt,
                 const hoedown_renderer_data *data)
{
    auto state = (hoedown_html_renderer_state *)data->opaque;

    if (!link || !link->size) return 0;

    if (link && link->size > 2 && link->data[0] == '%' && link->data[1] == '%') {
        renderMacro(ob, link, data, *(MacroData*)state->user_data);
        return 1;
    }

    HOEDOWN_BUFPUTSL(ob, "data, link->size);
    HOEDOWN_BUFPUTSL(ob, "\" alt=\"");

    if (alt && alt->size)
        hoedown_escape_html(ob, alt->data, alt->size, 0);

    if (title && title->size) {
        HOEDOWN_BUFPUTSL(ob, "\" title=\"");
        hoedown_escape_html(ob, title->data, title->size, 0); }

    hoedown_buffer_puts(ob, "\"/>");
    return 1;
}

// Link handler
int handleLink(hoedown_buffer *ob,
               const hoedown_buffer *content,
               const hoedown_buffer *link,
               const hoedown_buffer *title,
               const hoedown_renderer_data *data)
{
    string linkSource;
    if (link && link->size)
        linkSource = string(link->data, link->data + link->size);

    //cerr << "Rendering link " << linkSource << endl;

    if (linkSource.rfind(".md") == linkSource.size() - 3
        && linkSource.find("://") == string::npos) {
        linkSource += ".html";
        //cerr << "  rendering as " << linkSource << endl;
    }

    auto state = (hoedown_html_renderer_state *)data->opaque;

    HOEDOWN_BUFPUTSL(ob, "size) {
        HOEDOWN_BUFPUTSL(ob, "\" title=\"");
        hoedown_escape_html(ob, title->data, title->size, 0);
    }

    if (state->link_attributes) {
        hoedown_buffer_putc(ob, '\"');
        state->link_attributes(ob, link, data);
        hoedown_buffer_putc(ob, '>');
    } else {
        HOEDOWN_BUFPUTSL(ob, "\">");
    }

    if (content && content->size) hoedown_buffer_put(ob, content->data, content->size);
    HOEDOWN_BUFPUTSL(ob, "");
    return 1;
}

std::string renderMarkdown(const char * buf, size_t len,
                           MacroData & macroData)
{
    auto renderer = hoedown_html_renderer_new(HOEDOWN_HTML_USE_XHTML,
                                              0 /* data.toc_level */,
                                              &macroData);
    Scope_Exit(hoedown_html_renderer_free(renderer));

    renderer->normal_text = handleNormalText;
    renderer->image = handleImage;
    renderer->link = handleLink;

    /* Perform Markdown rendering */
    auto ob = hoedown_buffer_new(16384);
    Scope_Exit(hoedown_buffer_free(ob));

    int extensions
        = HOEDOWN_EXT_MATH
        | HOEDOWN_EXT_TABLES
        | HOEDOWN_EXT_FENCED_CODE;

    auto document = hoedown_document_new(renderer,
                                         (hoedown_extensions)extensions,
                                         10 /*data.max_nesting*/);
    Scope_Exit(hoedown_document_free(document));

    hoedown_document_render(document, ob, (const uint8_t *)buf, len);

    std::string result((const char *)ob->data, (const char *)(ob->data + ob->size));
    return result;
}

std::string renderMarkdown(const std::string & str, MacroData & macroData)
{
    return renderMarkdown(str.c_str(), str.length(), macroData);
}

RestRequestRouter::OnProcessRequest
getStaticRouteHandler(string dir, MldbServer * server, bool hideInternalEntities)
{
    if (dir.find("://") == string::npos)
        dir = "file://" + dir;

    return [dir, server, hideInternalEntities] (RestConnection & connection,
                  const RestRequest & request,
                  const RestRequestParsingContext & context)
        {

            string path = context.resources.back().rawData();

            if (path.find("..") != string::npos) {
                throw ML::Exception("not dealing with path with .. in it");
            }

            string filename = dir + "/" + path;

            //cerr << "looking for " << filename << " for resource " << path << endl;

            auto sendFile = [&] (const std::string & filename,
                                 const std::string & mimeType)
                {
                    if (!tryGetUriObjectInfo(filename)) {
                        connection.sendErrorResponse
                        (404,
                         "File '" + filename + "' doesn't exist", "text/plain");
                        return RestRequestRouter::MR_YES;
                    }

                    string filenameToLoad = filename;
                    if (filenameToLoad.find("file://") == 0)
                        filenameToLoad = string(filenameToLoad, 7);

                    cerr << "Loading file " << filename << " as " << filenameToLoad << endl;
                    ML::File_Read_Buffer buf(filenameToLoad);

                    string result(buf.start(), buf.end());
                    connection.sendResponse(200, result, mimeType);
                    return RestRequestRouter::MR_YES;
                };

            //cerr << "getting filename " << filename << endl;

            bool isMdHtml = false;

            if (filename.rfind(".md") == filename.size() - 3
                || (isMdHtml = (filename.rfind(".md.html") == filename.size() - 8))) {
                string mimeType;
                mimeType = "text/html";

                //if (tryGetUriObjectInfo(filename)) {
                //    return sendFile(filename, mimeType);
                //}

                string markdownFile = filename;
                if (isMdHtml) {
                    // Strip off the last 5 characters, ie .html
                    markdownFile = string(filename, 0, filename.length() - 5);
                }

                if (!tryGetUriObjectInfo(markdownFile)) {
                    connection.sendErrorResponse
                        (404,
                         "File '" + markdownFile + "' doesn't exist", "text/plain");
                    return RestRequestRouter::MR_YES;
                }

                MacroData macroData;
                macroData.dir = dir;
                macroData.server = server;
                macroData.hideInternalEntities = hideInternalEntities;

                string filenameToLoad = markdownFile;
                if (filenameToLoad.find("file://") == 0)
                    filenameToLoad = string(filenameToLoad, 7);

                cerr << "Loading file " << markdownFile << " as " << filenameToLoad << endl;

                ML::File_Read_Buffer buf(filenameToLoad);

                // Render the document
                std::string result;
                result += "<!DOCTYPE html>\n";
                result += "\n";
                result += "\n";
                result += "<meta charset='utf-8' />\n";
                result += "<title>MLDB Documentation</title>\n";
                result += "\n";
                result += "<link rel='stylesheet' href='/resources/css/prism.css'>\n";
                result += "<link rel='stylesheet' href='/resources/css/doc.css'>\n";
                result += "\n";
                result += "\n";
                result += "\n";
                result += "\n";
                result += "\n";
                result += renderMarkdown(buf.start(), buf.size(), macroData);
                result += "\n";
                result += "\n";

                connection.sendResponse(200, result, mimeType);
                return RestRequestRouter::MR_YES;
            }

            if (!tryGetUriObjectInfo(filename)) {
                connection.sendErrorResponse
                    (404,
                     "File '" + filename + "' doesn't exist", "text/plain");
                return RestRequestRouter::MR_YES;
            }

            string mimeType = "text/plain";
            if (filename.find(".html") != string::npos) {
                mimeType = "text/html";
            }
            else if (filename.find(".js") != string::npos) {
                mimeType = "application/javascript";
            }
            else if (filename.find(".css") != string::npos) {
                mimeType = "text/css";
            }
            else if (filename.find(".svg") != string::npos) {
                mimeType = "image/svg+xml";
            }
            return sendFile(filename, mimeType);
        };
}

void serveDocumentationDirectory(RestRequestRouter & parent,
                                 const std::string & route,
                                 const std::string & dir,
                                 MldbServer * server,
                                 bool hideInternalEntities)
{
    parent.addRoute(Rx(route + "/(.*)", "<resource>"),
                    "GET", "Static content",
                    getStaticRouteHandler(dir, server, hideInternalEntities),
                    Json::Value());
}

} // namespace MLDB
} // namespace Datacratic
<commit_msg>TRIVIAL formatting<commit_after>// This file is part of MLDB. Copyright 2015 Datacratic. All rights reserved.

/** static_content_handler.cc
    Jeremy Barnes, 5 March 2015
    Copyright (c) 2015 Datacratic Inc.  All rights reserved.

    Handler for static content.
*/

#include <typeinfo>
#include "mldb/server/static_content_handler.h"
#include "mldb/rest/rest_request_router.h"
#include "mldb/vfs/fs_utils.h"
#include "mldb/jml/utils/file_functions.h"
#include "mldb/ext/hoedown/src/buffer.h"
#include "mldb/ext/hoedown/src/html.h"
#include "mldb/ext/hoedown/src/document.h"
#include "mldb/ext/hoedown/src/escape.h"
#include "mldb/rest/in_process_rest_connection.h"
#include "mldb/server/mldb_server.h"
#include "mldb/core/mldb_entity.h"
#include "static_content_macro.h"
#include "mldb/base/scope.h"

using namespace std;

namespace Datacratic {
namespace MLDB {

void renderMacro(hoedown_buffer *ob,
                 const hoedown_buffer *text,
                 const hoedown_renderer_data *,
                 const MacroData & macroData)
{
    MacroContext context(&macroData, ob, text);

    string s((const char *)text->data, 2, text->size - 2);
    auto pos = s.find(' ');
    string name(s, 0, pos);
    string args(s, pos + 1);
    callMacro(context, name, args);
}

void handleNormalText (hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
{
    if (!text)
        return;

    auto state = (hoedown_html_renderer_state *)data->opaque;

    if (text->data && text->size > 2 && text->data[0] == '%' && text->data[1] == '%') {
        renderMacro(ob, text, data, *(MacroData*)state->user_data);
        return;
    }

    hoedown_escape_html(ob, text->data, text->size, 0);

}

int handleImage (hoedown_buffer *ob,
                 const hoedown_buffer *link,
                 const hoedown_buffer *title,
                 const hoedown_buffer *alt,
                 const hoedown_renderer_data *data)
{
    auto state = (hoedown_html_renderer_state *)data->opaque;

    if (!link || !link->size) return 0;

    if (link && link->size > 2 && link->data[0] == '%' && link->data[1] == '%') {
        renderMacro(ob, link, data, *(MacroData*)state->user_data);
        return 1;
    }

    HOEDOWN_BUFPUTSL(ob, "data, link->size);
    HOEDOWN_BUFPUTSL(ob, "\" alt=\"");

    if (alt && alt->size)
        hoedown_escape_html(ob, alt->data, alt->size, 0);

    if (title && title->size) {
        HOEDOWN_BUFPUTSL(ob, "\" title=\"");
        hoedown_escape_html(ob, title->data, title->size, 0); }

    hoedown_buffer_puts(ob, "\"/>");
    return 1;
}

// Link handler
int handleLink(hoedown_buffer *ob,
               const hoedown_buffer *content,
               const hoedown_buffer *link,
               const hoedown_buffer *title,
               const hoedown_renderer_data *data)
{
    string linkSource;
    if (link && link->size)
        linkSource = string(link->data, link->data + link->size);

    if (linkSource.rfind(".md") == linkSource.size() - 3
        && linkSource.find("://") == string::npos) {
        linkSource += ".html";
    }

    auto state = (hoedown_html_renderer_state *)data->opaque;

    HOEDOWN_BUFPUTSL(ob, "size) {
        HOEDOWN_BUFPUTSL(ob, "\" title=\"");
        hoedown_escape_html(ob, title->data, title->size, 0);
    }

    if (state->link_attributes) {
        hoedown_buffer_putc(ob, '\"');
        state->link_attributes(ob, link, data);
        hoedown_buffer_putc(ob, '>');
    } else {
        HOEDOWN_BUFPUTSL(ob, "\">");
    }

    if (content && content->size) hoedown_buffer_put(ob, content->data,
                                                     content->size);
    HOEDOWN_BUFPUTSL(ob, "");
    return 1;
}

std::string renderMarkdown(const char * buf, size_t len,
                           MacroData & macroData)
{
    auto renderer = hoedown_html_renderer_new(HOEDOWN_HTML_USE_XHTML,
                                              0 /* data.toc_level */,
                                              &macroData);
    Scope_Exit(hoedown_html_renderer_free(renderer));

    renderer->normal_text = handleNormalText;
    renderer->image = handleImage;
    renderer->link = handleLink;

    /* Perform Markdown rendering */
    auto ob = hoedown_buffer_new(16384);
    Scope_Exit(hoedown_buffer_free(ob));

    int extensions
        = HOEDOWN_EXT_MATH
        | HOEDOWN_EXT_TABLES
        | HOEDOWN_EXT_FENCED_CODE;

    auto document = hoedown_document_new(renderer,
                                         (hoedown_extensions)extensions,
                                         10 /*data.max_nesting*/);
    Scope_Exit(hoedown_document_free(document));

    hoedown_document_render(document, ob, (const uint8_t *)buf, len);

    std::string result((const char *)ob->data, (const char *)(ob->data + ob->size));
    return result;
}

std::string renderMarkdown(const std::string & str, MacroData & macroData)
{
    return renderMarkdown(str.c_str(), str.length(), macroData);
}

RestRequestRouter::OnProcessRequest
getStaticRouteHandler(string dir, MldbServer * server, bool hideInternalEntities)
{
    if (dir.find("://") == string::npos)
        dir = "file://" + dir;

    return [dir, server, hideInternalEntities] (RestConnection & connection,
                  const RestRequest & request,
                  const RestRequestParsingContext & context)
        {

            string path = context.resources.back().rawData();

            if (path.find("..") != string::npos) {
                throw ML::Exception("not dealing with path with .. in it");
            }

            string filename = dir + "/" + path;

            //cerr << "looking for " << filename << " for resource " << path << endl;

            auto sendFile = [&] (const std::string & filename,
                                 const std::string & mimeType)
                {
                    if (!tryGetUriObjectInfo(filename)) {
                        connection.sendErrorResponse
                        (404,
                         "File '" + filename + "' doesn't exist", "text/plain");
                        return RestRequestRouter::MR_YES;
                    }

                    string filenameToLoad = filename;
                    if (filenameToLoad.find("file://") == 0)
                        filenameToLoad = string(filenameToLoad, 7);

                    cerr << "Loading file " << filename << " as " << filenameToLoad << endl;
                    ML::File_Read_Buffer buf(filenameToLoad);

                    string result(buf.start(), buf.end());
                    connection.sendResponse(200, result, mimeType);
                    return RestRequestRouter::MR_YES;
                };

            //cerr << "getting filename " << filename << endl;

            bool isMdHtml = false;

            if (filename.rfind(".md") == filename.size() - 3
                || (isMdHtml = (filename.rfind(".md.html") == filename.size() - 8))) {
                string mimeType;
                mimeType = "text/html";

                //if (tryGetUriObjectInfo(filename)) {
                //    return sendFile(filename, mimeType);
                //}

                string markdownFile = filename;
                if (isMdHtml) {
                    // Strip off the last 5 characters, ie .html
                    markdownFile = string(filename, 0, filename.length() - 5);
                }

                if (!tryGetUriObjectInfo(markdownFile)) {
                    connection.sendErrorResponse
                        (404,
                         "File '" + markdownFile + "' doesn't exist", "text/plain");
                    return RestRequestRouter::MR_YES;
                }

                MacroData macroData;
                macroData.dir = dir;
                macroData.server = server;
                macroData.hideInternalEntities = hideInternalEntities;

                string filenameToLoad = markdownFile;
                if (filenameToLoad.find("file://") == 0)
                    filenameToLoad = string(filenameToLoad, 7);

                cerr << "Loading file " << markdownFile << " as " << filenameToLoad << endl;

                ML::File_Read_Buffer buf(filenameToLoad);

                // Render the document
                std::string result;
                result += "<!DOCTYPE html>\n";
                result += "\n";
                result += "\n";
                result += "<meta charset='utf-8' />\n";
                result += "<title>MLDB Documentation</title>\n";
                result += "\n";
                result += "<link rel='stylesheet' href='/resources/css/prism.css'>\n";
                result += "<link rel='stylesheet' href='/resources/css/doc.css'>\n";
                result += "\n";
                result += "\n";
                result += "\n";
                result += "\n";
                result += "\n";
                result += renderMarkdown(buf.start(), buf.size(), macroData);
                result += "\n";
                result += "\n";

                connection.sendResponse(200, result, mimeType);
                return RestRequestRouter::MR_YES;
            }

            if (!tryGetUriObjectInfo(filename)) {
                connection.sendErrorResponse
                    (404,
                     "File '" + filename + "' doesn't exist", "text/plain");
                return RestRequestRouter::MR_YES;
            }

            string mimeType = "text/plain";
            if (filename.find(".html") != string::npos) {
                mimeType = "text/html";
            }
            else if (filename.find(".js") != string::npos) {
                mimeType = "application/javascript";
            }
            else if (filename.find(".css") != string::npos) {
                mimeType = "text/css";
            }
            else if (filename.find(".svg") != string::npos) {
                mimeType = "image/svg+xml";
            }
            return sendFile(filename, mimeType);
        };
}

void serveDocumentationDirectory(RestRequestRouter & parent,
                                 const std::string & route,
                                 const std::string & dir,
                                 MldbServer * server,
                                 bool hideInternalEntities)
{
    parent.addRoute(Rx(route + "/(.*)", "<resource>"),
                    "GET", "Static content",
                    getStaticRouteHandler(dir, server, hideInternalEntities),
                    Json::Value());
}

} // namespace MLDB
} // namespace Datacratic
<|endoftext|>


<commit_before>#ifndef BIT_MEMORY_ALLOCATORS_DETAIL_BUMP_UP_ALLOCATOR_INL
#define BIT_MEMORY_ALLOCATORS_DETAIL_BUMP_UP_ALLOCATOR_INL

//============================================================================
// bump_up_allocator
//============================================================================

//----------------------------------------------------------------------------
// Constructors
//----------------------------------------------------------------------------

inline bit::memory::bump_up_allocator::bump_up_allocator( memory_block block )
  noexcept
  : m_block(block),
    m_current(m_block.data())
{
  assert( m_block && "Block must not be null" );
}

//----------------------------------------------------------------------------
// Allocation / Deallocation
//----------------------------------------------------------------------------

inline void* bit::memory::bump_up_allocator::try_allocate( std::size_t size,
                                                           std::size_t align,
                                                           std::size_t offset )
  noexcept
{
  assert( size && "cannot allocate 0 bytes");
  assert( align && "cannot allocate with 0 alignment");
  assert( is_power_of_two(align) && "alignment must be a power of two" );

  using byte_t = unsigned char;

  auto adjust = std::size_t{};
  auto* p = offset_align_forward(m_current,align,offset,&adjust);

  auto* p_end = static_cast<byte_t*>(p) + size;

  // If allocated outside the range, return nullptr
  if( BIT_MEMORY_UNLIKELY( p_end > m_block.end_address() ) )
    return nullptr;

  // bump the pointer
  m_current = p_end;

  return p_end;
}

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

inline void bit::memory::bump_up_allocator::deallocate( void* p,
                                                        std::size_t size )
{
  BIT_MEMORY_UNUSED(p);
  BIT_MEMORY_UNUSED(size);

  assert( m_block.contains( p ) && "Pointer must be contained by block" );

  // bump_up_allocator only uses truncated deallocations with deallocate_all
}

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

inline void bit::memory::bump_up_allocator::deallocate_all()
  noexcept
{
  m_current = m_block.data();
}

//----------------------------------------------------------------------------
// Observers
//----------------------------------------------------------------------------

inline bool bit::memory::bump_up_allocator::owns( void* p )
  const noexcept
{
  return m_block.start_address() <= p && p < m_current;
}

//----------------------------------------------------------------------------
// Comparisons
//----------------------------------------------------------------------------

bool bit::memory::operator==( const bump_up_allocator& lhs,
                              const bump_up_allocator& rhs )
  noexcept
{
  return lhs.m_current == rhs.m_current && lhs.m_block == rhs.m_block;
}

bool bit::memory::operator!=( const bump_up_allocator& lhs,
                              const bump_up_allocator& rhs )
  noexcept
{
  return !(lhs==rhs);
}

#endif /* BIT_MEMORY_ALLOCATORS_DETAIL_BUMP_UP_ALLOCATOR_INL */
<commit_msg>Fixed implementation of bump_up_allocator<commit_after>#ifndef BIT_MEMORY_ALLOCATORS_DETAIL_BUMP_UP_ALLOCATOR_INL
#define BIT_MEMORY_ALLOCATORS_DETAIL_BUMP_UP_ALLOCATOR_INL

//============================================================================
// bump_up_allocator
//============================================================================

//----------------------------------------------------------------------------
// Constructors
//----------------------------------------------------------------------------

inline bit::memory::bump_up_allocator::bump_up_allocator( memory_block block )
  noexcept
  : m_block(block),
    m_current(m_block.data())
{
  assert( m_block && "Block must not be null" );
}

//----------------------------------------------------------------------------
// Allocation / Deallocation
//----------------------------------------------------------------------------

inline void* bit::memory::bump_up_allocator::try_allocate( std::size_t size,
                                                           std::size_t align,
                                                           std::size_t offset )
  noexcept
{
  assert( size && "cannot allocate 0 bytes");
  assert( align && "cannot allocate with 0 alignment");
  assert( is_power_of_two(align) && "alignment must be a power of two" );

  using byte_t = unsigned char;

  auto adjust = std::size_t{};
  auto* p = offset_align_forward(m_current,align,offset,&adjust);

  auto* p_end = static_cast<byte_t*>(p) + size;

  // If allocated outside the range, return nullptr
  if( BIT_MEMORY_UNLIKELY( p_end > m_block.end_address() ) )
    return nullptr;

  // bump the pointer
  m_current = p_end;

  return p;
}

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

inline void bit::memory::bump_up_allocator::deallocate( void* p,
                                                        std::size_t size )
{
  BIT_MEMORY_UNUSED(p);
  BIT_MEMORY_UNUSED(size);

  assert( m_block.contains( p ) && "Pointer must be contained by block" );

  // bump_up_allocator only uses truncated deallocations with deallocate_all
}

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

inline void bit::memory::bump_up_allocator::deallocate_all()
  noexcept
{
  m_current = m_block.data();
}

//----------------------------------------------------------------------------
// Observers
//----------------------------------------------------------------------------

inline bool bit::memory::bump_up_allocator::owns( void* p )
  const noexcept
{
  return m_block.start_address() <= p && p < m_current;
}

//----------------------------------------------------------------------------
// Comparisons
//----------------------------------------------------------------------------

bool bit::memory::operator==( const bump_up_allocator& lhs,
                              const bump_up_allocator& rhs )
  noexcept
{
  return lhs.m_current == rhs.m_current && lhs.m_block == rhs.m_block;
}

bool bit::memory::operator!=( const bump_up_allocator& lhs,
                              const bump_up_allocator& rhs )
  noexcept
{
  return !(lhs==rhs);
}

#endif /* BIT_MEMORY_ALLOCATORS_DETAIL_BUMP_UP_ALLOCATOR_INL */
<|endoftext|>


<commit_before>/******************************************

	Author: Tiago Britto Lobão
	tiago.blobao@gmail.com
*/

/*
	Purpose: Control an integrated circuit
	Cirrus Logic - CS5490

	Used to measure electrical quantities

	MIT License

******************************************/

#include "CS5490.h"

/******* Init CS5490 *******/

//For Arduino & ESP8622
#if !(defined ARDUINO_NodeMCU_32S ) && !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(ARDUINO_Node32s)
	CS5490::CS5490(float mclk, int rx, int tx){
		this->MCLK = mclk;
		this->cSerial = new SoftwareSerial(rx,tx);
	}
//For ESP32 AND MEGA
#else
	CS5490::CS5490(float mclk){
		this->MCLK = mclk;
		this->cSerial = &Serial2;
	}
#endif

void CS5490::begin(int baudRate){
	cSerial->begin(baudRate);
	delay(10); //Avoid Bugs on Arduino UNO
}

/**************************************************************/
/*                     PRIVATE METHODS                        */
/**************************************************************/

/******* Write a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::write(int page, int address, long value){

	uint8_t checksum = 0;
	for(int i=0; i<3; i++)
		checksum += 0xFF - checksum;

	//Select page and address
	uint8_t buffer = (pageByte | (uint8_t)page);
	cSerial->write(buffer);
	buffer = (writeByte | (uint8_t)address);
	cSerial->write(buffer);

	//Send information
	for(int i=0; i<3 ; i++){
		data[i] = value & 0x000000FF;
		cSerial->write(this->data[i]);
		value >>= 8;
	}
	//Calculate and send checksum
	buffer = 0xFF - data[0] - data[1] - data[2];
	cSerial->write(buffer);
}

/******* Read a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::read(int page, int address){

	this->clearSerialBuffer();

	uint8_t buffer = (pageByte | (uint8_t)page);
	cSerial->write(buffer);
	buffer = (readByte | (uint8_t)address);
	cSerial->write(buffer);

	//Wait for 3 bytes to arrive
	while(cSerial->available() < 3);
	for(int i=0; i<3; i++){
		data[i] = cSerial->read();
	}
}

/******* Give an instruction by the serial communication *******/

void CS5490::instruct(int value){
	uint8_t buffer = (instructionByte | (uint8_t)value);
	cSerial->write(buffer);
}

/******* Clears cSerial Buffer *******/
void CS5490::clearSerialBuffer(){
	while (cSerial->available()) cSerial->read();
}

/*
  Function: toDouble
  Transforms a 24 bit number to a double number for easy processing data

  Param:
  data[] => Array with size 3. Each uint8_t is an 8 byte number received from CS5490
  LSBpow => Exponent specified from datasheet of the less significant bit
  MSBoption => Information of most significant bit case. It can be only three values:
    MSBnull (1)  The MSB is a Don't Care bit
    MSBsigned (2) the MSB is a negative value, requiring a 2 complement conversion
    MSBunsigned (3) The MSB is a positive value, the default case.

*/
double CS5490::toDouble(int LSBpow, int MSBoption){

	uint32_t buffer = 0;
	double output = 0.0;
	bool MSB;

	//Concat bytes in a 32 bit word
	buffer += this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;

  switch(MSBoption){

    case MSBnull:
      this->data[2] &= ~(1 << 7); //Clear MSB
      buffer += this->data[2] << 16;
      output = (double)buffer;
      output /= pow(2,LSBpow);
    break;

    case MSBsigned:
      MSB = data[2] & 0x80;
  		if(MSB){  //- (2 complement conversion)
  			buffer = ~buffer;
  			//Clearing the first 8 bits
  			for(int i=24; i<32; i++)
  			  buffer &= ~(1 << i);
  			output = (double)buffer + 1.0;
  			output /= -pow(2,LSBpow);
  		}
  		else{     //+
  		  output = (double)buffer;
  			output /= (pow(2,LSBpow)-1.0);
  		}
    break;

    default:
    case MSBunsigned:
      output = (double)buffer;
  		output /= pow(2,LSBpow);
    break;

  }

	return output;
}

/**************************************************************/
/*              PUBLIC METHODS - Instructions                 */
/**************************************************************/
void CS5490::reset(){
	this->instruct(1);
}

void CS5490::standby(){
	this->instruct(2);
}

void CS5490::wakeUp(){
	this->instruct(3);
}

void CS5490::singConv(){
	this->instruct(20);
}

void CS5490::contConv(){
	this->instruct(21);
}

void CS5490::haltConv(){
	this->instruct(24);
}

/**************************************************************/
/*       PUBLIC METHODS - Calibration and Configuration       */
/**************************************************************/

/* SET */
void CS5490::setBaudRate(long value){

	//Calculate the correct binary value
	uint32_t hexBR = ceil(value*0.5242880/MCLK);
	if (hexBR > 65535) hexBR = 65535;
	hexBR += 0x020000;

	this->write(0x80,0x07,hexBR);
	delay(100); //To avoid bugs from ESP32

	//Reset Serial communication from controller
	cSerial->end();
	cSerial->begin(value);
	return;
}

/* GET */
int CS5490::getGainI(){
	//Page 16, Address 33
	this->read(16,33);
	return this->toDouble(22,MSBunsigned);
}

long CS5490::getBaudRate(){
	this->read(0,7);
	uint32_t buffer = this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;
	buffer -= 0x020000;
	return ( (buffer/0.5242880)*MCLK );
}

/**************************************************************/
/*              PUBLIC METHODS - Measurements                 */
/**************************************************************/

double CS5490::getPeakV(){
	//Page 0, Address 36
	this->read(0,36);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPeakI(){
	//Page 0, Address 37
	this->read(0,37);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstI(){
	//Page 16, Address 2
	this->read(16,2);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstV(){
	//Page 16, Address 3
	this->read(16,3);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstP(){
	//Page 16, Address 4
	this->read(16,4);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getRmsI(){
	//Page 16, Address 6
	this->read(16,6);
	return this->toDouble(24, MSBunsigned);
}

double CS5490::getRmsV(){
	//Page 16, Address 7
	this->read(16,7);
	return this->toDouble(24, MSBunsigned);
}

double CS5490::getAvgP(){
	//Page 16, Address 5
	this->read(16,5);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgQ(){
	//Page 16, Address 14
	this->read(16,14);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgS(){
	//Page 16, Address 20
	this->read(16,20);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstQ(){
	//Page 16, Address 15
	this->read(16,15);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPF(){
	//Page 16, Address 21
	this->read(16,21);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalP(){
	//Page 16, Address 29
	this->read(16,29);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalS(){
	//Page 16, Address 30
	this->read(16,30);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalQ(){
	//Page 16, Address 31
	this->read(16,31);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getFreq(){
	//Page 16, Address 49
	this->read(16,49);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTime(){
	//Page 16, Address 61
	this->read(16,61);
	return this->toDouble(0, MSBunsigned);
}

/**************************************************************/
/*              PUBLIC METHODS - Read Register                */
/**************************************************************/

long CS5490::readReg(int page, int address){
	long value = 0;
	this->read(page, address);
	for(int i=0; i<3; i++){
		value += data[2-i];
		value <<= 8;
	}
	return value;
}
<commit_msg>Bug Fix for Arduino MEGA<commit_after>/******************************************

	Author: Tiago Britto Lobão
	tiago.blobao@gmail.com
*/

/*
	Purpose: Control an integrated circuit
	Cirrus Logic - CS5490

	Used to measure electrical quantities

	MIT License

******************************************/

#include "CS5490.h"

/******* Init CS5490 *******/

//For Arduino & ESP8622
#if !(defined ARDUINO_NodeMCU_32S ) && !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(ARDUINO_Node32s)
	CS5490::CS5490(float mclk, int rx, int tx){
		this->MCLK = mclk;
		this->cSerial = new SoftwareSerial(rx,tx);
	}
//For ESP32 AND MEGA
#else
	CS5490::CS5490(float mclk){
		this->MCLK = mclk;
		this->cSerial = &Serial2;
	}
#endif

void CS5490::begin(int baudRate){
	cSerial->begin(baudRate);
	delay(10); //Avoid Bugs on Arduino UNO
}

/**************************************************************/
/*                     PRIVATE METHODS                        */
/**************************************************************/

/******* Write a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::write(int page, int address, long value){

	uint8_t checksum = 0;
	for(int i=0; i<3; i++)
		checksum += 0xFF - checksum;

	//Select page and address
	uint8_t buffer = (pageByte | (uint8_t)page);
	cSerial->write(buffer);
	buffer = (writeByte | (uint8_t)address);
	cSerial->write(buffer);

	//Send information
	for(int i=0; i<3 ; i++){
		data[i] = value & 0x000000FF;
		cSerial->write(this->data[i]);
		value >>= 8;
	}
	//Calculate and send checksum
	buffer = 0xFF - data[0] - data[1] - data[2];
	cSerial->write(buffer);
}

/******* Read a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::read(int page, int address){

	this->clearSerialBuffer();

	uint8_t buffer = (pageByte | (uint8_t)page);
	cSerial->write(buffer);
	buffer = (readByte | (uint8_t)address);
	cSerial->write(buffer);

	//Wait for 3 bytes to arrive
	while(cSerial->available() < 3);
	for(int i=0; i<3; i++){
		data[i] = cSerial->read();
	}
}

/******* Give an instruction by the serial communication *******/

void CS5490::instruct(int value){
	uint8_t buffer = (instructionByte | (uint8_t)value);
	cSerial->write(buffer);
}

/******* Clears cSerial Buffer *******/
void CS5490::clearSerialBuffer(){
	while (cSerial->available()) cSerial->read();
}

/*
  Function: toDouble
  Transforms a 24 bit number to a double number for easy processing data

  Param:
  data[] => Array with size 3. Each uint8_t is an 8 byte number received from CS5490
  LSBpow => Exponent specified from datasheet of the less significant bit
  MSBoption => Information of most significant bit case. It can be only three values:
    MSBnull (1)  The MSB is a Don't Care bit
    MSBsigned (2) the MSB is a negative value, requiring a 2 complement conversion
    MSBunsigned (3) The MSB is a positive value, the default case.

*/
double CS5490::toDouble(int LSBpow, int MSBoption){

	uint32_t buffer = 0;
	double output = 0.0;
	bool MSB;

	//Concat bytes in a 32 bit word
	buffer += this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;

  switch(MSBoption){

    case MSBnull:
      this->data[2] &= ~(1 << 7); //Clear MSB
      buffer += this->data[2] << 16;
      output = (double)buffer;
      output /= pow(2,LSBpow);
    break;

    case MSBsigned:
      MSB = data[2] & 0x80;
  		if(MSB){  //- (2 complement conversion)
  			buffer = ~buffer;
  			//Clearing the first 8 bits
  			for(int i=24; i<32; i++)
  			  buffer &= ~(1 << i);
  			output = (double)buffer + 1.0;
  			output /= -pow(2,LSBpow);
  		}
  		else{     //+
  		  output = (double)buffer;
  			output /= (pow(2,LSBpow)-1.0);
  		}
    break;

    default:
    case MSBunsigned:
      output = (double)buffer;
  		output /= pow(2,LSBpow);
    break;

  }

	return output;
}

/**************************************************************/
/*              PUBLIC METHODS - Instructions                 */
/**************************************************************/
void CS5490::reset(){
	this->instruct(1);
}

void CS5490::standby(){
	this->instruct(2);
}

void CS5490::wakeUp(){
	this->instruct(3);
}

void CS5490::singConv(){
	this->instruct(20);
}

void CS5490::contConv(){
	this->instruct(21);
}

void CS5490::haltConv(){
	this->instruct(24);
}

/**************************************************************/
/*       PUBLIC METHODS - Calibration and Configuration       */
/**************************************************************/

/* SET */
void CS5490::setBaudRate(long value){

	//Calculate the correct binary value
	uint32_t hexBR = ceil(value*0.5242880/MCLK);
	if (hexBR > 65535) hexBR = 65535;
	hexBR += 0x020000;

	this->write(0x80,0x07,hexBR);
	delay(100); //To avoid bugs from ESP32

	//Reset Serial communication from controller
	cSerial->end();
	cSerial->begin(value);
	delay(50); //Avoid bugs from Arduino MEGA
	return;
}

/* GET */
int CS5490::getGainI(){
	//Page 16, Address 33
	this->read(16,33);
	return this->toDouble(22,MSBunsigned);
}

long CS5490::getBaudRate(){
	this->read(0,7);
	uint32_t buffer = this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;
	buffer -= 0x020000;
	return ( (buffer/0.5242880)*MCLK );
}

/**************************************************************/
/*              PUBLIC METHODS - Measurements                 */
/**************************************************************/

double CS5490::getPeakV(){
	//Page 0, Address 36
	this->read(0,36);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPeakI(){
	//Page 0, Address 37
	this->read(0,37);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstI(){
	//Page 16, Address 2
	this->read(16,2);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstV(){
	//Page 16, Address 3
	this->read(16,3);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstP(){
	//Page 16, Address 4
	this->read(16,4);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getRmsI(){
	//Page 16, Address 6
	this->read(16,6);
	return this->toDouble(24, MSBunsigned);
}

double CS5490::getRmsV(){
	//Page 16, Address 7
	this->read(16,7);
	return this->toDouble(24, MSBunsigned);
}

double CS5490::getAvgP(){
	//Page 16, Address 5
	this->read(16,5);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgQ(){
	//Page 16, Address 14
	this->read(16,14);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgS(){
	//Page 16, Address 20
	this->read(16,20);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstQ(){
	//Page 16, Address 15
	this->read(16,15);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPF(){
	//Page 16, Address 21
	this->read(16,21);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalP(){
	//Page 16, Address 29
	this->read(16,29);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalS(){
	//Page 16, Address 30
	this->read(16,30);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalQ(){
	//Page 16, Address 31
	this->read(16,31);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getFreq(){
	//Page 16, Address 49
	this->read(16,49);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTime(){
	//Page 16, Address 61
	this->read(16,61);
	return this->toDouble(0, MSBunsigned);
}

/**************************************************************/
/*              PUBLIC METHODS - Read Register                */
/**************************************************************/

long CS5490::readReg(int page, int address){
	long value = 0;
	this->read(page, address);
	for(int i=0; i<3; i++){
		value += data[2-i];
		value <<= 8;
	}
	return value;
}
<|endoftext|>


<commit_before>#include "communique/Client.h"

#include <mutex>
#include <future>
#include "communique/impl/Exceptions.h"

#define _WEBSOCKETPP_CPP11_STL_ // Make sure websocketpp uses c++11 features in preference to boost ones
#include <websocketpp/client.hpp>
#include <websocketpp/config/asio.hpp>
#include "communique/impl/Connection.h"

//
// Declaration of the pimple
//
namespace communique
{
	class ClientPrivateMembers
	{
	public:
		typedef websocketpp::client<websocketpp::config::asio_tls> client_type;
		std::thread ioThread_;
		client_type client_;
		std::auto_ptr<communique::impl::Connection> pConnection_;

		std::function<void(const std::string&)> infoHandler_;
		std::function<void(const std::string&,communique::IConnection*)> infoHandlerAdvanced_;
		std::function<std::string(const std::string&)> requestHandler_;
		std::function<std::string(const std::string&,communique::IConnection*)> requestHandlerAdvanced_;

		std::shared_ptr<boost::asio::ssl::context> on_tls_init( websocketpp::connection_hdl hdl );
//		void on_message( websocketpp::connection_hdl hdl, client_type::message_ptr msg );
		void on_open( websocketpp::connection_hdl hdl );
		void on_close( websocketpp::connection_hdl hdl );
		void on_interrupt( websocketpp::connection_hdl hdl );
	};
}

communique::Client::Client()
	: pImple_( new ClientPrivateMembers )
{
	pImple_->client_.set_access_channels(websocketpp::log::alevel::none);
	//pImple_->client_.set_error_channels(websocketpp::log::elevel::all ^ websocketpp::log::elevel::info);
	pImple_->client_.set_error_channels(websocketpp::log::elevel::none);
	pImple_->client_.set_tls_init_handler( std::bind( &ClientPrivateMembers::on_tls_init, pImple_.get(), std::placeholders::_1 ) );
	pImple_->client_.init_asio();
//	pImple_->client_.set_message_handler( std::bind( &ClientPrivateMembers::on_message, pImple_.get(), std::placeholders::_1, std::placeholders::_2 ) );
	pImple_->client_.set_open_handler( std::bind( &ClientPrivateMembers::on_open, pImple_.get(), std::placeholders::_1 ) );
	pImple_->client_.set_close_handler( std::bind( &ClientPrivateMembers::on_close, pImple_.get(), std::placeholders::_1 ) );
	pImple_->client_.set_interrupt_handler( std::bind( &ClientPrivateMembers::on_interrupt, pImple_.get(), std::placeholders::_1 ) );
}

communique::Client::Client( Client&& otherClient ) noexcept
	: pImple_( std::move(otherClient.pImple_) )
{
	// No operation, everything done in initialiser list
}

communique::Client::~Client()
{
	try
	{
		// If std::move is used then pImple_ can be null, in which
		// case I don't want to do any cleanup.
		if( pImple_ ) disconnect();
	}
	catch(...) { /* Make sure no exceptions propagate out */ }
}

std::shared_ptr<boost::asio::ssl::context> communique::ClientPrivateMembers::on_tls_init( websocketpp::connection_hdl hdl )
{
	websocketpp::lib::shared_ptr<boost::asio::ssl::context> pContext( new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1) );
	pContext->set_options( boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use );
	//pContext->set_password_callback( boost::bind( &server::get_password, this ) );
	pContext->use_certificate_chain_file( "/Users/phmag/Documents/EclipseWorkspaces/temp/keys/server.pem" );
	pContext->use_private_key_file( "/Users/phmag/Documents/EclipseWorkspaces/temp/keys/server.pem", boost::asio::ssl::context::pem );
	pContext->load_verify_file( "/Users/phmag/Documents/EclipseWorkspaces/temp/keys/rootCA.pem" );
	pContext->use_tmp_dh_file( "/Users/phmag/Documents/EclipseWorkspaces/temp/dh512.pem" );
	//pContext->set_verify_mode( boost::asio::ssl::verify_peer/* | boost::asio::ssl::verify_fail_if_no_peer_cert*/ );

	return pContext;
}

void communique::Client::connect( const std::string& URI )
{
	websocketpp::lib::error_code errorCode;
	auto pWebPPConnection=pImple_->client_.get_connection( URI, errorCode );
	if( errorCode.value()!=0 ) throw std::runtime_error( "Unable to get the websocketpp connection - "+errorCode.message() );
	pImple_->pConnection_.reset( new communique::impl::Connection( pWebPPConnection, pImple_->infoHandler_, pImple_->requestHandler_ ) );
	// If "advanced" handlers have beed set (just has IConnection* as additional argument)
	// change the handler to these.
	if( pImple_->infoHandlerAdvanced_ ) pImple_->pConnection_->setInfoHandler(pImple_->infoHandlerAdvanced_);
	if( pImple_->requestHandlerAdvanced_ ) pImple_->pConnection_->setRequestHandler(pImple_->requestHandlerAdvanced_);

	if( errorCode )
	{
		pImple_->client_.get_alog().write(websocketpp::log::alevel::app,errorCode.message());
		throw communique::impl::Exception( errorCode.message() );
	}

	pImple_->client_.connect( pImple_->pConnection_->underlyingPointer() );
	pImple_->ioThread_=std::thread( &ClientPrivateMembers::client_type::run, &pImple_->client_ );
}

void communique::Client::disconnect()
{
	if( pImple_->pConnection_.get()!=nullptr ) pImple_->pConnection_->close();
	if( pImple_->ioThread_.joinable() ) pImple_->ioThread_.join();
}

void communique::Client::sendRequest( const std::string& message, std::function<void(const std::string&)> responseHandler )
{
	if( !pImple_->pConnection_.get() ) throw communique::impl::Exception( "No connection" );
	pImple_->pConnection_->sendRequest( message, responseHandler );
}

void communique::Client::sendInfo( const std::string& message )
{
	if( !pImple_->pConnection_.get() ) throw communique::impl::Exception( "No connection" );
	pImple_->pConnection_->sendInfo( message );
}

void communique::Client::setInfoHandler( std::function<void(const std::string&)> infoHandler )
{
	pImple_->infoHandler_=infoHandler;
	pImple_->infoHandlerAdvanced_=nullptr;
	if( pImple_->pConnection_.get() ) // don't know why I need ".get()". C++11 standard should allow auto_ptr comparison
	{
		pImple_->pConnection_->setInfoHandler( infoHandler );
	}
}

void communique::Client::setInfoHandler( std::function<void(const std::string&,communique::IConnection*)> infoHandler )
{
	pImple_->infoHandler_=nullptr;
	pImple_->infoHandlerAdvanced_=infoHandler;
	if( pImple_->pConnection_.get() ) // don't know why I need ".get()". C++11 standard should allow auto_ptr comparison
	{
		pImple_->pConnection_->setInfoHandler( infoHandler );
	}
}

void communique::Client::setRequestHandler( std::function<std::string(const std::string&)> requestHandler )
{
	pImple_->requestHandler_=requestHandler;
	pImple_->requestHandlerAdvanced_=nullptr;
	if( pImple_->pConnection_.get() )
	{
		pImple_->pConnection_->setRequestHandler( requestHandler );
	}
}

void communique::Client::setRequestHandler( std::function<std::string(const std::string&,communique::IConnection*)> requestHandler )
{
	pImple_->requestHandler_=nullptr;
	pImple_->requestHandlerAdvanced_=requestHandler;
	if( pImple_->pConnection_.get() )
	{
		pImple_->pConnection_->setRequestHandler( requestHandler );
	}
}

//void communique::ClientPrivateMembers::on_message( websocketpp::connection_hdl hdl, client_type::message_ptr msg )
//{
//	communique::MessageHeader messageHeader;
//	messageHeader.ParseFromString( msg->get_payload() );
//
//	if( messageHeader.type()==communique::MessageHeader::INFO )
//	{
//		if( infoHandler_ ) infoHandler_( msg->get_payload().substr(messageHeader.ByteSize()) );
//	}
//	else if( messageHeader.type()==communique::MessageHeader::REQUEST )
//	{
//		if( requestHandler_ && messageHeader.has_user_reference() )
//		{
//			// Record where the message header stops and the body starts
//			size_t messageBodyOffset=messageHeader.ByteSize();
//
//			// Reuse the message header and stream out as a string
//			messageHeader.set_type( communique::MessageHeader::RESPONSE );
//			// The user reference will already be set correctly
//			std::string headerString;
//			messageHeader.SerializeToString( &headerString );
//
//			std::async( [ this, hdl, msg, messageBodyOffset, headerString ]()
//			{
//				// Send the rest of the message with the header stripped off first, and use the
//				// return from the handler
//				client_.send( hdl, headerString+requestHandler_( msg->get_payload().substr(messageBodyOffset) ), websocketpp::frame::opcode::BINARY );
//			} );
//		}
//	}
//	else if( messageHeader.type()==communique::MessageHeader::RESPONSE )
//	{
//		// This will be the response to a request that I've sent out, so
//		// I need to search for the handler that was stored when the message
//		// was sent.
//
//		// TODO - implement
//	}
//}
//
void communique::ClientPrivateMembers::on_open( websocketpp::connection_hdl hdl )
{
}

void communique::ClientPrivateMembers::on_close( websocketpp::connection_hdl hdl )
{
}

void communique::ClientPrivateMembers::on_interrupt( websocketpp::connection_hdl hdl )
{
	std::cout << "Connection has been interrupted" << std::endl;
}
<commit_msg>Change auto_ptr to unique_ptr. Don't know why I had that there.<commit_after>#include "communique/Client.h"

#include <mutex>
#include <future>
#include "communique/impl/Exceptions.h"

#define _WEBSOCKETPP_CPP11_STL_ // Make sure websocketpp uses c++11 features in preference to boost ones
#include <websocketpp/client.hpp>
#include <websocketpp/config/asio.hpp>
#include "communique/impl/Connection.h"

//
// Declaration of the pimple
//
namespace communique
{
	class ClientPrivateMembers
	{
	public:
		typedef websocketpp::client<websocketpp::config::asio_tls> client_type;
		std::thread ioThread_;
		client_type client_;
		std::unique_ptr<communique::impl::Connection> pConnection_;

		std::function<void(const std::string&)> infoHandler_;
		std::function<void(const std::string&,communique::IConnection*)> infoHandlerAdvanced_;
		std::function<std::string(const std::string&)> requestHandler_;
		std::function<std::string(const std::string&,communique::IConnection*)> requestHandlerAdvanced_;

		std::shared_ptr<boost::asio::ssl::context> on_tls_init( websocketpp::connection_hdl hdl );
//		void on_message( websocketpp::connection_hdl hdl, client_type::message_ptr msg );
		void on_open( websocketpp::connection_hdl hdl );
		void on_close( websocketpp::connection_hdl hdl );
		void on_interrupt( websocketpp::connection_hdl hdl );
	};
}

communique::Client::Client()
	: pImple_( new ClientPrivateMembers )
{
	pImple_->client_.set_access_channels(websocketpp::log::alevel::none);
	//pImple_->client_.set_error_channels(websocketpp::log::elevel::all ^ websocketpp::log::elevel::info);
	pImple_->client_.set_error_channels(websocketpp::log::elevel::none);
	pImple_->client_.set_tls_init_handler( std::bind( &ClientPrivateMembers::on_tls_init, pImple_.get(), std::placeholders::_1 ) );
	pImple_->client_.init_asio();
//	pImple_->client_.set_message_handler( std::bind( &ClientPrivateMembers::on_message, pImple_.get(), std::placeholders::_1, std::placeholders::_2 ) );
	pImple_->client_.set_open_handler( std::bind( &ClientPrivateMembers::on_open, pImple_.get(), std::placeholders::_1 ) );
	pImple_->client_.set_close_handler( std::bind( &ClientPrivateMembers::on_close, pImple_.get(), std::placeholders::_1 ) );
	pImple_->client_.set_interrupt_handler( std::bind( &ClientPrivateMembers::on_interrupt, pImple_.get(), std::placeholders::_1 ) );
}

communique::Client::Client( Client&& otherClient ) noexcept
	: pImple_( std::move(otherClient.pImple_) )
{
	// No operation, everything done in initialiser list
}

communique::Client::~Client()
{
	try
	{
		// If std::move is used then pImple_ can be null, in which
		// case I don't want to do any cleanup.
		if( pImple_ ) disconnect();
	}
	catch(...) { /* Make sure no exceptions propagate out */ }
}

std::shared_ptr<boost::asio::ssl::context> communique::ClientPrivateMembers::on_tls_init( websocketpp::connection_hdl hdl )
{
	websocketpp::lib::shared_ptr<boost::asio::ssl::context> pContext( new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1) );
	pContext->set_options( boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use );
	//pContext->set_password_callback( boost::bind( &server::get_password, this ) );
	pContext->use_certificate_chain_file( "/Users/phmag/Documents/EclipseWorkspaces/temp/keys/server.pem" );
	pContext->use_private_key_file( "/Users/phmag/Documents/EclipseWorkspaces/temp/keys/server.pem", boost::asio::ssl::context::pem );
	pContext->load_verify_file( "/Users/phmag/Documents/EclipseWorkspaces/temp/keys/rootCA.pem" );
	pContext->use_tmp_dh_file( "/Users/phmag/Documents/EclipseWorkspaces/temp/dh512.pem" );
	//pContext->set_verify_mode( boost::asio::ssl::verify_peer/* | boost::asio::ssl::verify_fail_if_no_peer_cert*/ );

	return pContext;
}

void communique::Client::connect( const std::string& URI )
{
	websocketpp::lib::error_code errorCode;
	auto pWebPPConnection=pImple_->client_.get_connection( URI, errorCode );
	if( errorCode.value()!=0 ) throw std::runtime_error( "Unable to get the websocketpp connection - "+errorCode.message() );
	pImple_->pConnection_.reset( new communique::impl::Connection( pWebPPConnection, pImple_->infoHandler_, pImple_->requestHandler_ ) );
	// If "advanced" handlers have beed set (just has IConnection* as additional argument)
	// change the handler to these.
	if( pImple_->infoHandlerAdvanced_ ) pImple_->pConnection_->setInfoHandler(pImple_->infoHandlerAdvanced_);
	if( pImple_->requestHandlerAdvanced_ ) pImple_->pConnection_->setRequestHandler(pImple_->requestHandlerAdvanced_);

	if( errorCode )
	{
		pImple_->client_.get_alog().write(websocketpp::log::alevel::app,errorCode.message());
		throw communique::impl::Exception( errorCode.message() );
	}

	pImple_->client_.connect( pImple_->pConnection_->underlyingPointer() );
	pImple_->ioThread_=std::thread( &ClientPrivateMembers::client_type::run, &pImple_->client_ );
}

void communique::Client::disconnect()
{
	if( pImple_->pConnection_ ) pImple_->pConnection_->close();
	if( pImple_->ioThread_.joinable() ) pImple_->ioThread_.join();
}

void communique::Client::sendRequest( const std::string& message, std::function<void(const std::string&)> responseHandler )
{
	if( !pImple_->pConnection_ ) throw communique::impl::Exception( "No connection" );
	pImple_->pConnection_->sendRequest( message, responseHandler );
}

void communique::Client::sendInfo( const std::string& message )
{
	if( pImple_->pConnection_ ) throw communique::impl::Exception( "No connection" );
	pImple_->pConnection_->sendInfo( message );
}

void communique::Client::setInfoHandler( std::function<void(const std::string&)> infoHandler )
{
	pImple_->infoHandler_=infoHandler;
	pImple_->infoHandlerAdvanced_=nullptr;
	if( pImple_->pConnection_ )
	{
		pImple_->pConnection_->setInfoHandler( infoHandler );
	}
}

void communique::Client::setInfoHandler( std::function<void(const std::string&,communique::IConnection*)> infoHandler )
{
	pImple_->infoHandler_=nullptr;
	pImple_->infoHandlerAdvanced_=infoHandler;
	if( pImple_->pConnection_ )
	{
		pImple_->pConnection_->setInfoHandler( infoHandler );
	}
}

void communique::Client::setRequestHandler( std::function<std::string(const std::string&)> requestHandler )
{
	pImple_->requestHandler_=requestHandler;
	pImple_->requestHandlerAdvanced_=nullptr;
	if( pImple_->pConnection_ )
	{
		pImple_->pConnection_->setRequestHandler( requestHandler );
	}
}

void communique::Client::setRequestHandler( std::function<std::string(const std::string&,communique::IConnection*)> requestHandler )
{
	pImple_->requestHandler_=nullptr;
	pImple_->requestHandlerAdvanced_=requestHandler;
	if( pImple_->pConnection_ )
	{
		pImple_->pConnection_->setRequestHandler( requestHandler );
	}
}

//void communique::ClientPrivateMembers::on_message( websocketpp::connection_hdl hdl, client_type::message_ptr msg )
//{
//	communique::MessageHeader messageHeader;
//	messageHeader.ParseFromString( msg->get_payload() );
//
//	if( messageHeader.type()==communique::MessageHeader::INFO )
//	{
//		if( infoHandler_ ) infoHandler_( msg->get_payload().substr(messageHeader.ByteSize()) );
//	}
//	else if( messageHeader.type()==communique::MessageHeader::REQUEST )
//	{
//		if( requestHandler_ && messageHeader.has_user_reference() )
//		{
//			// Record where the message header stops and the body starts
//			size_t messageBodyOffset=messageHeader.ByteSize();
//
//			// Reuse the message header and stream out as a string
//			messageHeader.set_type( communique::MessageHeader::RESPONSE );
//			// The user reference will already be set correctly
//			std::string headerString;
//			messageHeader.SerializeToString( &headerString );
//
//			std::async( [ this, hdl, msg, messageBodyOffset, headerString ]()
//			{
//				// Send the rest of the message with the header stripped off first, and use the
//				// return from the handler
//				client_.send( hdl, headerString+requestHandler_( msg->get_payload().substr(messageBodyOffset) ), websocketpp::frame::opcode::BINARY );
//			} );
//		}
//	}
//	else if( messageHeader.type()==communique::MessageHeader::RESPONSE )
//	{
//		// This will be the response to a request that I've sent out, so
//		// I need to search for the handler that was stored when the message
//		// was sent.
//
//		// TODO - implement
//	}
//}
//
void communique::ClientPrivateMembers::on_open( websocketpp::connection_hdl hdl )
{
}

void communique::ClientPrivateMembers::on_close( websocketpp::connection_hdl hdl )
{
}

void communique::ClientPrivateMembers::on_interrupt( websocketpp::connection_hdl hdl )
{
	std::cout << "Connection has been interrupted" << std::endl;
}
<|endoftext|>


<commit_before>#pragma once
#include "spinner/resmgr.hpp"

namespace boom {
	//! 当たり判定属性マスク
	using CMask = uint32_t;
	using CTime = uint32_t;
	using Int_OP = spn::Optional<int>;

	/*! コリジョン情報を纏めた構造体
		\tparam CMGR	コリジョンマネージャ
		\tparam HLMDL	モデルハンドル(Locked)
		\tparam BCID	BroadCollisionクラスのオブジェクトを特定できるようなデータ型
		\tparam UD		任意のユーザーデータ型
	*/
	template <class CMGR, class HLMDL, class BCID, class UD>
	class ColMem {
		private:
			CMask	_mask;	//!< 当たり判定対象フラグ
			struct Index {
				CTime	time;	//!< indexが指している累積時間
				Int_OP	front,	//!< 線形リスト先頭インデックス
						last;	//!< 線形リスト末尾インデックス

				//! リストに何もつないでない状態に初期化
				void init(CTime tm) {
					time = tm;
					front = last = spn::none;
				}
				//! リスト末尾に新しくインデックスを加える
				/*!	\param[in] idx 新しく加えるインデックス値
					\return 直前の先頭インデックス値 */
				Int_OP setLastIndex(int idx) {
					Int_OP ret = last;
					if(!front)
						front = last = idx;
					else
						last = idx;
					return ret;
				}
			};
			Index		_cur,
						_prev;
			HLMDL		_hlMdl;
			BCID		_bcid;
			UD			_udata;

		public:
			template <class UD2>
			ColMem(CMask ms, HLMDL hm, UD2&& ud):
				_mask(ms),
				_hlMdl(hm),
				_udata(std::forward<UD2>(ud))
			{
				_cur.init(0);
				_prev.init(0);
			}
			void setBCID(const BCID& ns) {
				_bcid = ns;
			}
			const BCID& getBCID() const {
				return _bcid;
			}
			auto getBVolume() const -> decltype(_hlMdl.cref()->im_getBVolume()) {
				return _hlMdl.cref()->im_getBVolume();
			}
			auto getModel() const -> decltype(_hlMdl.cref().get()) {
				return _hlMdl.cref().get();
			}
			CMask getMask() const {
				return _mask;
			}
			UD& refUserData() {
				return _udata;
			}
			//! 衝突判定履歴を巡回 (コリジョン開始 / 継続中の判定)
			/*! \param cb[in,out] コールバック(CMgr::Hist) */
			template <class CB>
			void getCollision(CB&& cb) const {
				auto& m = CMGR::_ref();
				CTime tm = m.getAccum();
				// 現在の時刻より新しい時に巡回
				if(_cur.time >= tm && _cur.front)
					m.iterateHistCur(*_cur.front, std::forward<CB>(cb));
			}
			//! 衝突判定結果を参照 (コリジョン終了判定)
			/*! \param cb[in,out] コールバック(CMgr::Hist) */
			template <class CB>
			void getEndCollision(CB&& cb) const {
				auto& m = CMGR::_ref();
				CTime tm = m.getAccum();
				if(_cur.time == tm) {
					// 今回何かと衝突していて前回も判定済の場合
					if(_prev.time == tm-1 && _prev.front) {
						// _prevにあって_curに無い物が対象
						m.iterateHistPrev(*_prev.front, [&cb](const auto& h) {
							// _prevで一度参照されたエントリにはフラグが立ててある -> (nFrame==0)
							if(h.nFrame > 0)
								std::forward<CB>(cb)(h);
						});
					}
				} else if(_cur.time == tm-1 && _cur.front) {
					// 今回何とも衝突しなかった場合
					// _cur のリスト全部対象。ただし履歴はColMgr上では過去のものになってるのでPrevを参照
					m.iterateHistPrev(*_cur.front, std::forward<CB>(cb));
				}
			}
			//! 引数の時刻-1と合致する方のリストインデックス先頭を取得
			/*! \return どちらも無効ならspn::none */
			Int_OP getPrevIndex(CTime tm) const {
				if(_cur.time == tm-1)
					return _cur.front;
				if(_prev.time == tm-1)
					return _prev.front;
				return spn::none;
			}
			//! カレントリスト末尾に新しくインデックスを加える
			/*! \return 直前の先頭インデックス値 */
			Int_OP setLastIndex(CTime tm, int idx) {
				// もし新しい時刻の設定だったらカーソルの新旧入れ替え
				if(tm > _cur.time) {
					_prev = _cur;
					_cur.init(tm);
				}
				return _cur.setLastIndex(idx);
			}
	};
	/*!	\tparam	BC		BroadCollision class template<BVolume, IModel>
		\tparam	Types	geom2d::Types or geom3d::Types
		\tparam UD		userdata type
	*/
	template <template <class,class> class BC,
				class Types,
				class UD>
	class ColMgr : public spn::ResMgrA<ColMem<ColMgr<BC,Types,UD>,
											typename Types::MMgr::LHdl,
											typename BC<decltype(std::declval<typename Types::IModel>().im_getBVolume()),
														typename Types::IModel>
													  ::IDType,
											UD>,
										ColMgr<BC,Types,UD>>,
					public Types::Narrow
	{
		public:
			using IModel = typename Types::IModel;									//!< IModel interface
			using MMgr = typename Types::MMgr;										//!< ModelManager
			using user_t = UD;														//!< Userdata
			using BVolume = decltype(std::declval<IModel>().im_getBVolume());		//!< Bounding-volume
			using this_t = ColMgr<BC, Types, user_t>;
			using BroadC = BC<BVolume, IModel>;										//!< BroadCollision class
			using BCID = typename BroadC::IDType;									//!< BroadCollision dependant Id
			// ---- model handle type ----
			using HMdl = typename MMgr::SHdl;
			using HLMdl = typename MMgr::LHdl;
			using CMem = ColMem<this_t, HLMdl, BCID, user_t>;						//!< Collision information structure

			using base = spn::ResMgrA<CMem, this_t>;
			// --- collision handle type ----
			using HCol = typename base::SHdl;
			using HLCol = typename base::LHdl;
			// 単方向リスト
			struct Hist {
				HCol	hCol;
				int		nFrame;				//!< 衝突継続したフレーム数
				int		nextOffset;			//!< 次のエントリへのバイトオフセット

				Hist(HCol hc, int nf):
					hCol(hc),
					nFrame(nf),
					nextOffset(0)
				{}
			};
		private:
			using Narrow = typename Types::Narrow;
			using HistVec = std::vector<Hist>;
			using RemList = std::vector<HLCol>;

			BroadC		_broadC;
			CTime		_accum = 0;
			//! history, removelistの現在のインデックス
			int			_swHist = 0;
			HistVec		_hist[2];
			RemList		_rmList[2];
			bool		_bDelete = false;

			HistVec& _getCurHist() { return _hist[_swHist]; }
			HistVec& _getPrevHist() { return _hist[_swHist^1]; }
			const HistVec& _getCurHist() const { return _hist[_swHist]; }
			const HistVec& _getPrevHist() const { return _hist[_swHist^1]; }
			RemList& _getCurRM() { return _rmList[_swHist]; }
			RemList& _getPrevRM() { return _rmList[_swHist^1]; }
			using Hist_OP = spn::Optional<Hist&>;

			//! hc0のリストにhc1のエントリが存在するかチェック
			/*!	\return hc1が見つかればその参照を返す */
			Hist_OP _hasPrevCollision(HCol hc0, HCol hc1) {
				auto& c0 = hc0.cref();
				Hist_OP ret;
				if(auto pidx = c0.getPrevIndex(_accum)) {
					iterateHistPrev(*pidx, [hc1,&ret](Hist& h){
						if(h.hCol == hc1)
							ret = h;
					});
				}
				return ret;
			}
			/*! \param hv	_hist[0] or _hist[1]
				\param idx	巡回を開始するインデックス */
			template <class HVec, class CB>
			static void _IterateHist(HVec& hv, int idx, CB&& cb) {
				AssertP(Trap, idx>=0)
				auto* hst = &hv[idx];
				for(;;) {
					std::forward<CB>(cb)(*hst);
					int np = hst->nextOffset;
					if(np != 0)
						hst = reinterpret_cast<decltype(hst)>(reinterpret_cast<uintptr_t>(hst) + np);
					else break;
				}
			}
			HistVec& _switchHist() {
				_doRelease(false);
				_swHist ^= 1;
				_hist[_swHist].clear();
				return _hist[_swHist];
			}
			void _doRelease(bool bBoth) {
				//! 解放を一時的に遅延させていたハンドルを処理
				_bDelete = true;
				_getPrevRM().clear();
				if(bBoth)
					_getCurRM().clear();
				_bDelete = false;
			}
		public:
			ColMgr(): _broadC([this](spn::SHandle sh){
								return HCol(sh).cref().getBVolume(); })
			{
				Narrow::Initialize();
			}
			~ColMgr() {
				_doRelease(true);
			}
			CTime getAccum() const {
				return _accum;
			}
			template <class CB>
			void iterateHistCur(int idx, CB&& cb) const { _IterateHist(_getCurHist(), idx, std::forward<CB>(cb)); }
			template <class CB>
			void iterateHistPrev(int idx, CB&& cb) const { _IterateHist(_getPrevHist(), idx, std::forward<CB>(cb)); }
			template <class CB>
			void iterateHistCur(int idx, CB&& cb) { _IterateHist(_getCurHist(), idx, std::forward<CB>(cb)); }
			template <class CB>
			void iterateHistPrev(int idx, CB&& cb) { _IterateHist(_getPrevHist(), idx, std::forward<CB>(cb)); }

			//! 当たり判定対象を追加
			/*! \param ms[in] マスク */
			template <class UD2>
			HLCol addCol(CMask ms, HMdl hm, UD2&& ud=UD()) {
				HLCol hlC = base::emplace(ms, hm, std::forward<UD2>(ud));
				hlC.ref().setBCID(_broadC.add(hlC, ms));
				return std::move(hlC);
			}
			//! ハンドル解放処理を一時的に遅延させる (デストラクタ時以外)
			void release(HCol hCol) {
				if(!_bDelete && hCol.count() == 1) {
					// OnCollisionEndの処理がある為、次のupdateまでハンドル解放を遅延
					_getCurRM().push_back(hCol);
					// しかしBroadCからは即刻外す
					_broadC.rem(hCol.cref().getBCID());
				}
				base::release(hCol);
			}
			//! 時間を1進め、衝突履歴の更新
			void update() {
				auto& hist = _switchHist();

				++_accum;
				_broadC.broadCollision([this, &hist](spn::SHandle sh0, spn::SHandle sh1){
					// 同じハンドルという事はあり得ない筈
					AssertP(Trap, sh0!=sh1)

					HCol hc0(sh0), hc1(sh1);
					// 詳細判定
					// 毎フレームヒットリストを再構築
					if(Narrow::Hit(hc0.ref().getModel(), hc1.ref().getModel())) {
						auto fn = [this, &hist](HCol hc0, HCol hc1){
							auto &c0 = hc0.ref(),
								&c1 = hc1.ref();
							// 前のフレームでの継続フレームリストを探索
							int nf = 0;
							if(auto hist = _hasPrevCollision(hc0, hc1)) {
								nf = hist->nFrame;
								// 衝突が継続中なので目印としてnFrameを0にしておく
								hist->nFrame = 0;
							}
							int nextID = hist.size();
							Int_OP lastID = c0.setLastIndex(_accum, nextID);
							if(lastID)
								hist[*lastID].nextOffset = (nextID - *lastID) * sizeof(Hist);
							hist.push_back(Hist(hc1, nf+1));
						};
						fn(hc0, hc1);
						fn(hc1, hc0);
					}
				});
			}
	};
}
<commit_msg>ColMgr: spn::SHandleからの暗黙的なキャストに関するコンパイルエラー修正<commit_after>#pragma once
#include "spinner/resmgr.hpp"

namespace boom {
	//! 当たり判定属性マスク
	using CMask = uint32_t;
	using CTime = uint32_t;
	using Int_OP = spn::Optional<int>;

	/*! コリジョン情報を纏めた構造体
		\tparam CMGR	コリジョンマネージャ
		\tparam HLMDL	モデルハンドル(Locked)
		\tparam BCID	BroadCollisionクラスのオブジェクトを特定できるようなデータ型
		\tparam UD		任意のユーザーデータ型
	*/
	template <class CMGR, class HLMDL, class BCID, class UD>
	class ColMem {
		private:
			CMask	_mask;	//!< 当たり判定対象フラグ
			struct Index {
				CTime	time;	//!< indexが指している累積時間
				Int_OP	front,	//!< 線形リスト先頭インデックス
						last;	//!< 線形リスト末尾インデックス

				//! リストに何もつないでない状態に初期化
				void init(CTime tm) {
					time = tm;
					front = last = spn::none;
				}
				//! リスト末尾に新しくインデックスを加える
				/*!	\param[in] idx 新しく加えるインデックス値
					\return 直前の先頭インデックス値 */
				Int_OP setLastIndex(int idx) {
					Int_OP ret = last;
					if(!front)
						front = last = idx;
					else
						last = idx;
					return ret;
				}
			};
			Index		_cur,
						_prev;
			HLMDL		_hlMdl;
			BCID		_bcid;
			UD			_udata;

		public:
			template <class UD2>
			ColMem(CMask ms, HLMDL hm, UD2&& ud):
				_mask(ms),
				_hlMdl(hm),
				_udata(std::forward<UD2>(ud))
			{
				_cur.init(0);
				_prev.init(0);
			}
			void setBCID(const BCID& ns) {
				_bcid = ns;
			}
			const BCID& getBCID() const {
				return _bcid;
			}
			auto getBVolume() const -> decltype(_hlMdl.cref()->im_getBVolume()) {
				return _hlMdl.cref()->im_getBVolume();
			}
			auto getModel() const -> decltype(_hlMdl.cref().get()) {
				return _hlMdl.cref().get();
			}
			CMask getMask() const {
				return _mask;
			}
			UD& refUserData() {
				return _udata;
			}
			//! 衝突判定履歴を巡回 (コリジョン開始 / 継続中の判定)
			/*! \param cb[in,out] コールバック(CMgr::Hist) */
			template <class CB>
			void getCollision(CB&& cb) const {
				auto& m = CMGR::_ref();
				CTime tm = m.getAccum();
				// 現在の時刻より新しい時に巡回
				if(_cur.time >= tm && _cur.front)
					m.iterateHistCur(*_cur.front, std::forward<CB>(cb));
			}
			//! 衝突判定結果を参照 (コリジョン終了判定)
			/*! \param cb[in,out] コールバック(CMgr::Hist) */
			template <class CB>
			void getEndCollision(CB&& cb) const {
				auto& m = CMGR::_ref();
				CTime tm = m.getAccum();
				if(_cur.time == tm) {
					// 今回何かと衝突していて前回も判定済の場合
					if(_prev.time == tm-1 && _prev.front) {
						// _prevにあって_curに無い物が対象
						m.iterateHistPrev(*_prev.front, [&cb](const auto& h) {
							// _prevで一度参照されたエントリにはフラグが立ててある -> (nFrame==0)
							if(h.nFrame > 0)
								std::forward<CB>(cb)(h);
						});
					}
				} else if(_cur.time == tm-1 && _cur.front) {
					// 今回何とも衝突しなかった場合
					// _cur のリスト全部対象。ただし履歴はColMgr上では過去のものになってるのでPrevを参照
					m.iterateHistPrev(*_cur.front, std::forward<CB>(cb));
				}
			}
			//! 引数の時刻-1と合致する方のリストインデックス先頭を取得
			/*! \return どちらも無効ならspn::none */
			Int_OP getPrevIndex(CTime tm) const {
				if(_cur.time == tm-1)
					return _cur.front;
				if(_prev.time == tm-1)
					return _prev.front;
				return spn::none;
			}
			//! カレントリスト末尾に新しくインデックスを加える
			/*! \return 直前の先頭インデックス値 */
			Int_OP setLastIndex(CTime tm, int idx) {
				// もし新しい時刻の設定だったらカーソルの新旧入れ替え
				if(tm > _cur.time) {
					_prev = _cur;
					_cur.init(tm);
				}
				return _cur.setLastIndex(idx);
			}
	};
	/*!	\tparam	BC		BroadCollision class template<BVolume, IModel>
		\tparam	Types	geom2d::Types or geom3d::Types
		\tparam UD		userdata type
	*/
	template <template <class,class> class BC,
				class Types,
				class UD>
	class ColMgr : public spn::ResMgrA<ColMem<ColMgr<BC,Types,UD>,
											typename Types::MMgr::LHdl,
											typename BC<decltype(std::declval<typename Types::IModel>().im_getBVolume()),
														typename Types::IModel>
													  ::IDType,
											UD>,
										ColMgr<BC,Types,UD>>,
					public Types::Narrow
	{
		public:
			using IModel = typename Types::IModel;									//!< IModel interface
			using MMgr = typename Types::MMgr;										//!< ModelManager
			using user_t = UD;														//!< Userdata
			using BVolume = decltype(std::declval<IModel>().im_getBVolume());		//!< Bounding-volume
			using this_t = ColMgr<BC, Types, user_t>;
			using BroadC = BC<BVolume, IModel>;										//!< BroadCollision class
			using BCID = typename BroadC::IDType;									//!< BroadCollision dependant Id
			// ---- model handle type ----
			using HMdl = typename MMgr::SHdl;
			using HLMdl = typename MMgr::LHdl;
			using CMem = ColMem<this_t, HLMdl, BCID, user_t>;						//!< Collision information structure

			using base = spn::ResMgrA<CMem, this_t>;
			// --- collision handle type ----
			using HCol = typename base::SHdl;
			using HLCol = typename base::LHdl;
			// 単方向リスト
			struct Hist {
				HCol	hCol;
				int		nFrame;				//!< 衝突継続したフレーム数
				int		nextOffset;			//!< 次のエントリへのバイトオフセット

				Hist(HCol hc, int nf):
					hCol(hc),
					nFrame(nf),
					nextOffset(0)
				{}
			};
		private:
			using Narrow = typename Types::Narrow;
			using HistVec = std::vector<Hist>;
			using RemList = std::vector<HLCol>;

			BroadC		_broadC;
			CTime		_accum = 0;
			//! history, removelistの現在のインデックス
			int			_swHist = 0;
			HistVec		_hist[2];
			RemList		_rmList[2];
			bool		_bDelete = false;

			HistVec& _getCurHist() { return _hist[_swHist]; }
			HistVec& _getPrevHist() { return _hist[_swHist^1]; }
			const HistVec& _getCurHist() const { return _hist[_swHist]; }
			const HistVec& _getPrevHist() const { return _hist[_swHist^1]; }
			RemList& _getCurRM() { return _rmList[_swHist]; }
			RemList& _getPrevRM() { return _rmList[_swHist^1]; }
			using Hist_OP = spn::Optional<Hist&>;

			//! hc0のリストにhc1のエントリが存在するかチェック
			/*!	\return hc1が見つかればその参照を返す */
			Hist_OP _hasPrevCollision(HCol hc0, HCol hc1) {
				auto& c0 = hc0.cref();
				Hist_OP ret;
				if(auto pidx = c0.getPrevIndex(_accum)) {
					iterateHistPrev(*pidx, [hc1,&ret](Hist& h){
						if(h.hCol == hc1)
							ret = h;
					});
				}
				return ret;
			}
			/*! \param hv	_hist[0] or _hist[1]
				\param idx	巡回を開始するインデックス */
			template <class HVec, class CB>
			static void _IterateHist(HVec& hv, int idx, CB&& cb) {
				AssertP(Trap, idx>=0)
				auto* hst = &hv[idx];
				for(;;) {
					std::forward<CB>(cb)(*hst);
					int np = hst->nextOffset;
					if(np != 0)
						hst = reinterpret_cast<decltype(hst)>(reinterpret_cast<uintptr_t>(hst) + np);
					else break;
				}
			}
			HistVec& _switchHist() {
				_doRelease(false);
				_swHist ^= 1;
				auto& cur = _getCurHist();
				cur.clear();
				return cur;
			}
			void _doRelease(bool bBoth) {
				//! 解放を一時的に遅延させていたハンドルを処理
				_bDelete = true;
				_getPrevRM().clear();
				if(bBoth)
					_getCurRM().clear();
				_bDelete = false;
			}
			bool release(spn::SHandle) override {
				Assert(Throw, false, "invalid function call");
				return false;
			}
		public:
			ColMgr(): _broadC([this](spn::SHandle sh){
								return HCol::FromSHandle(sh).cref().getBVolume(); })
			{
				Narrow::Initialize();
			}
			~ColMgr() {
				_doRelease(true);
			}
			CTime getAccum() const {
				return _accum;
			}
			template <class CB>
			void iterateHistCur(int idx, CB&& cb) const { _IterateHist(_getCurHist(), idx, std::forward<CB>(cb)); }
			template <class CB>
			void iterateHistPrev(int idx, CB&& cb) const { _IterateHist(_getPrevHist(), idx, std::forward<CB>(cb)); }
			template <class CB>
			void iterateHistCur(int idx, CB&& cb) { _IterateHist(_getCurHist(), idx, std::forward<CB>(cb)); }
			template <class CB>
			void iterateHistPrev(int idx, CB&& cb) { _IterateHist(_getPrevHist(), idx, std::forward<CB>(cb)); }

			//! 当たり判定対象を追加
			/*! \param ms[in] マスク */
			template <class UD2>
			HLCol addCol(CMask ms, HMdl hm, UD2&& ud=UD()) {
				HLCol hlC = base::emplace(ms, hm, std::forward<UD2>(ud));
				hlC.ref().setBCID(_broadC.add(hlC, ms));
				return std::move(hlC);
			}
			//! ハンドル解放処理を一時的に遅延させる (デストラクタ時以外)
			void release(HCol hCol) {
				if(!_bDelete && hCol.count() == 1) {
					// OnCollisionEndの処理がある為、次のupdateまでハンドル解放を遅延
					_getCurRM().push_back(hCol);
					// しかしBroadCからは即刻外す
					_broadC.rem(hCol.cref().getBCID());
				}
				base::release(hCol);
			}
			//! 時間を1進め、衝突履歴の更新
			void update() {
				auto& hist = _switchHist();

				++_accum;
				_broadC.broadCollision([this, &hist](spn::SHandle sh0, spn::SHandle sh1){
					// 同じハンドルという事はあり得ない筈
					AssertP(Trap, sh0!=sh1)

					HCol hc0(HCol::FromSHandle(sh0)),
						hc1(HCol::FromSHandle(sh1));
					// 詳細判定
					// 毎フレームヒットリストを再構築
					if(Narrow::Hit(hc0.ref().getModel(), hc1.ref().getModel(), _accum)) {
						auto fn = [this, &hist](HCol hc0, HCol hc1){
							auto &c0 = hc0.ref(),
								&c1 = hc1.ref();
							// 前のフレームでの継続フレームリストを探索
							int nf = 0;
							if(auto hist = _hasPrevCollision(hc0, hc1)) {
								nf = hist->nFrame;
								// 衝突が継続中なので目印としてnFrameを0にしておく
								hist->nFrame = 0;
							}
							int nextID = hist.size();
							Int_OP lastID = c0.setLastIndex(_accum, nextID);
							if(lastID)
								hist[*lastID].nextOffset = (nextID - *lastID) * sizeof(Hist);
							hist.push_back(Hist(hc1, nf+1));
						};
						fn(hc0, hc1);
						fn(hc1, hc0);
					}
				});
			}
	};
}
<|endoftext|>


<commit_before>#pragma once

#include <map>
#include <string>

#include <boost/any.hpp>

#include "blackhole/error.hpp"
#include "cast.hpp"

namespace blackhole {

namespace aux {

template<class T>
struct extractor {
    boost::any source;
    std::string name;

    extractor(const boost::any& source, const std::string& name = T::name()) :
        source(source),
        name(name)
    {}

    extractor<T> operator [](const std::string& name) const {
        std::map<std::string, boost::any> map;
        try {
            to(map);
        } catch (const std::exception&) {
            throw error_t("can not extract '%s': '%s' is not map", name, this->name);
        }
        return extractor<T>(map[name], name);
    }

    template<typename R>
    void to(R& value) const {
        try{
            cast_traits<R>::to(source, value);
        } catch (boost::bad_any_cast&) {
            throw error_t("conversion error for member '%s'", name);
        }
    }

    template<typename R>
    R get() const {
        R value;
        to(value);
        return value;
    }
};

} // namespace aux

}  // namespace blackhole
<commit_msg>More verbose error message now returned with exception if failed to convert `boost::any` type to necessary.<commit_after>#pragma once

#include <map>
#include <string>

#include <boost/any.hpp>

#include "blackhole/error.hpp"
#include "cast.hpp"

namespace blackhole {

namespace aux {

template<class T>
struct extractor {
    boost::any source;
    std::string name;

    extractor(const boost::any& source, const std::string& name = T::name()) :
        source(source),
        name(name)
    {}

    extractor<T> operator [](const std::string& name) const {
        std::map<std::string, boost::any> map;
        try {
            to(map);
        } catch (const std::exception&) {
            throw error_t("can not extract '%s': '%s' is not map", name, this->name);
        }
        return extractor<T>(map[name], name);
    }

    template<typename R>
    void to(R& value) const {
        try {
            cast_traits<R>::to(source, value);
        } catch (const boost::bad_any_cast&) {
            throw error_t("can not extract '%s' field from '%s': member is absent or has different type", name, T::name());
        }
    }

    template<typename R>
    R get() const {
        R value;
        to(value);
        return value;
    }
};

} // namespace aux

}  // namespace blackhole
<|endoftext|>


<commit_before>#include "StdAfx.h"
#include "ArwDecoder.h"
/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009 Klaus Post

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

    http://www.klauspost.com
*/

namespace RawSpeed {

ArwDecoder::ArwDecoder(TiffIFD *rootIFD, FileMap* file) :
    RawDecoder(file), mRootIFD(rootIFD) {

}

ArwDecoder::~ArwDecoder(void) {
  if (mRootIFD)
    delete mRootIFD;
  mRootIFD = NULL;
}

RawImage ArwDecoder::decodeRawInternal() {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(STRIPOFFSETS);

  if (data.empty())
    ThrowRDE("ARW Decoder: No image data found");

  TiffIFD* raw = data[0];
  int compression = raw->getEntry(COMPRESSION)->getInt();
  if (32767 != compression)
    ThrowRDE("ARW Decoder: Unsupported compression");

  TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
  TiffEntry *counts = raw->getEntry(STRIPBYTECOUNTS);

  if (offsets->count != 1) {
    ThrowRDE("ARW Decoder: Multiple Strips found: %u", offsets->count);
  }
  if (counts->count != offsets->count) {
    ThrowRDE("ARW Decoder: Byte count number does not match strip size: count:%u, strips:%u ", counts->count, offsets->count);
  }
  uint32 width = raw->getEntry(IMAGEWIDTH)->getInt();
  uint32 height = raw->getEntry(IMAGELENGTH)->getInt();
  uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getInt();

  // Sony E-550 marks compressed 8bpp ARW with 12 bit per pixel
  // this makes the compression detect it as a ARW v1.
  // This camera has however another MAKER entry, so we MAY be able
  // to detect it this way in the future.
  data = mRootIFD->getIFDsWithTag(MAKE);
  string make = data[0]->getEntry(MAKE)->getString();
  if (!make.compare("SONY"))
    bitPerPixel = 8;

  bool arw1 = counts->getInt() * 8 != width * height * bitPerPixel;
  if (arw1)
    height += 8;

  mRaw->dim = iPoint2D(width, height);
  mRaw->createData();

  const ushort16* c = raw->getEntry(SONY_CURVE)->getShortArray();
  uint32 sony_curve[] = { 0, 0, 0, 0, 0, 4095 };

  for (uint32 i = 0; i < 4; i++)
    sony_curve[i+1] = (c[i] >> 2) & 0xfff;

  for (uint32 i = 0; i < 0x4001; i++)
    curve[i] = i;

  for (uint32 i = 0; i < 5; i++)
    for (uint32 j = sony_curve[i] + 1; j <= sony_curve[i+1]; j++)
      curve[j] = curve[j-1] + (1 << i);

  uint32 c2 = counts->getInt();
  uint32 off = offsets->getInt();

  if (!mFile->isValid(off))
    ThrowRDE("Sony ARW decoder: Data offset after EOF, file probably truncated");

  if (!mFile->isValid(off + c2))
    c2 = mFile->getSize() - off;

  ByteStream input(mFile->getData(off), c2);

  try {
    if (arw1)
      DecodeARW(input, width, height);
    else
      DecodeARW2(input, width, height, bitPerPixel);
  } catch (IOException &e) {
    mRaw->setError(e.what());
    // Let's ignore it, it may have delivered somewhat useful data.
  }

  return mRaw;
}

void ArwDecoder::DecodeARW(ByteStream &input, uint32 w, uint32 h) {
  BitPumpMSB bits(&input);
  uchar8* data = mRaw->getData();
  ushort16* dest = (ushort16*) & data[0];
  uint32 pitch = mRaw->pitch / sizeof(ushort16);
  int sum = 0;
  for (uint32 x = w; x--;)
    for (uint32 y = 0; y < h + 1; y += 2) {
      bits.checkPos();
      bits.fill();
      if (y == h) y = 1;
      uint32 len = 4 - bits.getBitsNoFill(2);
      if (len == 3 && bits.getBitNoFill()) len = 0;
      if (len == 4)
        while (len < 17 && !bits.getBitNoFill()) len++;
      int diff = bits.getBits(len);
      if ((diff & (1 << (len - 1))) == 0)
        diff -= (1 << len) - 1;
      sum += diff;
      _ASSERTE(!(sum >> 12));
      if (y < h) dest[x+y*pitch] = sum;
    }
}

void ArwDecoder::DecodeARW2(ByteStream &input, uint32 w, uint32 h, uint32 bpp) {

  if (bpp == 8) {
    in = &input;
    this->startThreads();
    return;
  } // End bpp = 8

  if (bpp == 12) {
    uchar8* data = mRaw->getData();
    uint32 pitch = mRaw->pitch;
    const uchar8 *in = input.getData();

    if (input.getRemainSize() < (w * 3 / 2))
      ThrowRDE("Sony Decoder: Image data section too small, file probably truncated");

    if (input.getRemainSize() < (w*h*3 / 2))
      h = input.getRemainSize() / (w * 3 / 2) - 1;

    for (uint32 y = 0; y < h; y++) {
      ushort16* dest = (ushort16*) & data[y*pitch];
      for (uint32 x = 0 ; x < w; x += 2) {
        uint32 g1 = *in++;
        uint32 g2 = *in++;
        // Shift up to match compressed precision
        dest[x] = (g1 | ((g2 & 0xf) << 8)) << 2;
        uint32 g3 = *in++;
        dest[x+1] = ((g2 >> 4) | (g3 << 4)) << 2;
      }
    }
    return;
  }
  ThrowRDE("Unsupported bit depth");
}

void ArwDecoder::checkSupportInternal(CameraMetaData *meta) {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(MODEL);
  if (data.empty())
    ThrowRDE("ARW Support check: Model name found");
  string make = data[0]->getEntry(MAKE)->getString();
  string model = data[0]->getEntry(MODEL)->getString();
  this->checkCameraSupported(meta, make, model, "");
}

void ArwDecoder::decodeMetaDataInternal(CameraMetaData *meta) {
  //Default
  int iso = 0;

  mRaw->cfa.setCFA(CFA_RED, CFA_GREEN, CFA_GREEN2, CFA_BLUE);
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(MODEL);

  if (data.empty())
    ThrowRDE("ARW Meta Decoder: Model name found");
  if (!data[0]->hasEntry(MAKE))
    ThrowRDE("ARW Decoder: Make name not found");

  string make = data[0]->getEntry(MAKE)->getString();
  string model = data[0]->getEntry(MODEL)->getString();

  if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
    iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getInt();

  setMetaData(meta, make, model, "", iso);
}

/* Since ARW2 compressed images have predictable offsets, we decode them threaded */

void ArwDecoder::decodeThreaded(RawDecoderThread * t) {
  uchar8* data = mRaw->getData();
  uint32 pitch = mRaw->pitch;
  uint32 w = mRaw->dim.x;

  BitPumpPlain bits(in);
  for (uint32 y = t->start_y; y < t->end_y; y++) {
    ushort16* dest = (ushort16*) & data[y*pitch];
    // Realign
    bits.setAbsoluteOffset((w*8*y) >> 3);

    // Process 32 pixels (16x2) per loop.
    for (uint32 x = 0; x < w - 30;) {
      bits.checkPos();
      int _max = bits.getBits(11);
      int _min = bits.getBits(11);
      int _imax = bits.getBits(4);
      int _imin = bits.getBits(4);
      int sh;
      for (sh = 0; sh < 4 && 0x80 << sh <= _max - _min; sh++);
      for (int i = 0; i < 16; i++) {
        int p;
        if (i == _imax) p = _max;
        else if (i == _imin) p = _min;
        else {
          p = (bits.getBits(7) << sh) + _min;
          if (p > 0x7ff)
            p = 0x7ff;
        }
        dest[x+i*2] = curve[p << 1];
      }
      x += x & 1 ? 31 : 1;  // Skip to next 32 pixels
    }
  }
}

} // namespace RawSpeed
<commit_msg>Fix images modified by Sony Image Data Converter SR. Regression tested.<commit_after>#include "StdAfx.h"
#include "ArwDecoder.h"
/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009 Klaus Post

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

    http://www.klauspost.com
*/

namespace RawSpeed {

ArwDecoder::ArwDecoder(TiffIFD *rootIFD, FileMap* file) :
    RawDecoder(file), mRootIFD(rootIFD) {

}

ArwDecoder::~ArwDecoder(void) {
  if (mRootIFD)
    delete mRootIFD;
  mRootIFD = NULL;
}

RawImage ArwDecoder::decodeRawInternal() {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(STRIPOFFSETS);

  if (data.empty())
    ThrowRDE("ARW Decoder: No image data found");

  TiffIFD* raw = data[0];
  int compression = raw->getEntry(COMPRESSION)->getInt();
  if (32767 != compression)
    ThrowRDE("ARW Decoder: Unsupported compression");

  TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
  TiffEntry *counts = raw->getEntry(STRIPBYTECOUNTS);

  if (offsets->count != 1) {
    ThrowRDE("ARW Decoder: Multiple Strips found: %u", offsets->count);
  }
  if (counts->count != offsets->count) {
    ThrowRDE("ARW Decoder: Byte count number does not match strip size: count:%u, strips:%u ", counts->count, offsets->count);
  }
  uint32 width = raw->getEntry(IMAGEWIDTH)->getInt();
  uint32 height = raw->getEntry(IMAGELENGTH)->getInt();
  uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getInt();

  // Sony E-550 marks compressed 8bpp ARW with 12 bit per pixel
  // this makes the compression detect it as a ARW v1.
  // This camera has however another MAKER entry, so we MAY be able
  // to detect it this way in the future.
  data = mRootIFD->getIFDsWithTag(MAKE);
  if (data.size() > 1) {
    for (uint32 i = 0; i < data.size(); i++) {
      string make = data[i]->getEntry(MAKE)->getString();
      /* Check for maker "SONY" without spaces */
      if (!make.compare("SONY"))
        bitPerPixel = 8;
    }
  }

  bool arw1 = counts->getInt() * 8 != width * height * bitPerPixel;
  if (arw1)
    height += 8;

  mRaw->dim = iPoint2D(width, height);
  mRaw->createData();

  const ushort16* c = raw->getEntry(SONY_CURVE)->getShortArray();
  uint32 sony_curve[] = { 0, 0, 0, 0, 0, 4095 };

  for (uint32 i = 0; i < 4; i++)
    sony_curve[i+1] = (c[i] >> 2) & 0xfff;

  for (uint32 i = 0; i < 0x4001; i++)
    curve[i] = i;

  for (uint32 i = 0; i < 5; i++)
    for (uint32 j = sony_curve[i] + 1; j <= sony_curve[i+1]; j++)
      curve[j] = curve[j-1] + (1 << i);

  uint32 c2 = counts->getInt();
  uint32 off = offsets->getInt();

  if (!mFile->isValid(off))
    ThrowRDE("Sony ARW decoder: Data offset after EOF, file probably truncated");

  if (!mFile->isValid(off + c2))
    c2 = mFile->getSize() - off;

  ByteStream input(mFile->getData(off), c2);

  try {
    if (arw1)
      DecodeARW(input, width, height);
    else
      DecodeARW2(input, width, height, bitPerPixel);
  } catch (IOException &e) {
    mRaw->setError(e.what());
    // Let's ignore it, it may have delivered somewhat useful data.
  }

  return mRaw;
}

void ArwDecoder::DecodeARW(ByteStream &input, uint32 w, uint32 h) {
  BitPumpMSB bits(&input);
  uchar8* data = mRaw->getData();
  ushort16* dest = (ushort16*) & data[0];
  uint32 pitch = mRaw->pitch / sizeof(ushort16);
  int sum = 0;
  for (uint32 x = w; x--;)
    for (uint32 y = 0; y < h + 1; y += 2) {
      bits.checkPos();
      bits.fill();
      if (y == h) y = 1;
      uint32 len = 4 - bits.getBitsNoFill(2);
      if (len == 3 && bits.getBitNoFill()) len = 0;
      if (len == 4)
        while (len < 17 && !bits.getBitNoFill()) len++;
      int diff = bits.getBits(len);
      if ((diff & (1 << (len - 1))) == 0)
        diff -= (1 << len) - 1;
      sum += diff;
      _ASSERTE(!(sum >> 12));
      if (y < h) dest[x+y*pitch] = sum;
    }
}

void ArwDecoder::DecodeARW2(ByteStream &input, uint32 w, uint32 h, uint32 bpp) {

  if (bpp == 8) {
    in = &input;
    this->startThreads();
    return;
  } // End bpp = 8

  if (bpp == 12) {
    uchar8* data = mRaw->getData();
    uint32 pitch = mRaw->pitch;
    const uchar8 *in = input.getData();

    if (input.getRemainSize() < (w * 3 / 2))
      ThrowRDE("Sony Decoder: Image data section too small, file probably truncated");

    if (input.getRemainSize() < (w*h*3 / 2))
      h = input.getRemainSize() / (w * 3 / 2) - 1;

    for (uint32 y = 0; y < h; y++) {
      ushort16* dest = (ushort16*) & data[y*pitch];
      for (uint32 x = 0 ; x < w; x += 2) {
        uint32 g1 = *in++;
        uint32 g2 = *in++;
        // Shift up to match compressed precision
        dest[x] = (g1 | ((g2 & 0xf) << 8)) << 2;
        uint32 g3 = *in++;
        dest[x+1] = ((g2 >> 4) | (g3 << 4)) << 2;
      }
    }
    return;
  }
  ThrowRDE("Unsupported bit depth");
}

void ArwDecoder::checkSupportInternal(CameraMetaData *meta) {
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(MODEL);
  if (data.empty())
    ThrowRDE("ARW Support check: Model name found");
  string make = data[0]->getEntry(MAKE)->getString();
  string model = data[0]->getEntry(MODEL)->getString();
  this->checkCameraSupported(meta, make, model, "");
}

void ArwDecoder::decodeMetaDataInternal(CameraMetaData *meta) {
  //Default
  int iso = 0;

  mRaw->cfa.setCFA(CFA_RED, CFA_GREEN, CFA_GREEN2, CFA_BLUE);
  vector<TiffIFD*> data = mRootIFD->getIFDsWithTag(MODEL);

  if (data.empty())
    ThrowRDE("ARW Meta Decoder: Model name found");
  if (!data[0]->hasEntry(MAKE))
    ThrowRDE("ARW Decoder: Make name not found");

  string make = data[0]->getEntry(MAKE)->getString();
  string model = data[0]->getEntry(MODEL)->getString();

  if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
    iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getInt();

  setMetaData(meta, make, model, "", iso);
}

/* Since ARW2 compressed images have predictable offsets, we decode them threaded */

void ArwDecoder::decodeThreaded(RawDecoderThread * t) {
  uchar8* data = mRaw->getData();
  uint32 pitch = mRaw->pitch;
  uint32 w = mRaw->dim.x;

  BitPumpPlain bits(in);
  for (uint32 y = t->start_y; y < t->end_y; y++) {
    ushort16* dest = (ushort16*) & data[y*pitch];
    // Realign
    bits.setAbsoluteOffset((w*8*y) >> 3);

    // Process 32 pixels (16x2) per loop.
    for (uint32 x = 0; x < w - 30;) {
      bits.checkPos();
      int _max = bits.getBits(11);
      int _min = bits.getBits(11);
      int _imax = bits.getBits(4);
      int _imin = bits.getBits(4);
      int sh;
      for (sh = 0; sh < 4 && 0x80 << sh <= _max - _min; sh++);
      for (int i = 0; i < 16; i++) {
        int p;
        if (i == _imax) p = _max;
        else if (i == _imin) p = _min;
        else {
          p = (bits.getBits(7) << sh) + _min;
          if (p > 0x7ff)
            p = 0x7ff;
        }
        dest[x+i*2] = curve[p << 1];
      }
      x += x & 1 ? 31 : 1;  // Skip to next 32 pixels
    }
  }
}

} // namespace RawSpeed
<|endoftext|>